open-agents-ai 0.103.75 → 0.103.76

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/dist/index.js +137 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -19173,7 +19173,8 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
19173
19173
  "skill_build",
19174
19174
  "skill_execute",
19175
19175
  "sub_agent",
19176
- "priority_delegate"
19176
+ "priority_delegate",
19177
+ "ask_user"
19177
19178
  ].includes(tc.name);
19178
19179
  const cachedResult = recentToolResults.get(toolFingerprint);
19179
19180
  if (isReadLike && cachedResult !== void 0) {
@@ -19447,7 +19448,7 @@ Take a DIFFERENT action now.`
19447
19448
  });
19448
19449
  break;
19449
19450
  }
19450
- const narratedToolPattern = /(?:```(?:bash|json|sh)?\s*\n?)?\b(file_read|file_write|file_edit|shell|skill_execute|skill_list|skill_build|grep_search|find_files|web_search|web_fetch|task_complete|memory_read|memory_write|list_directory)\b[\s(=\-]/;
19451
+ const narratedToolPattern = /(?:```(?:bash|json|sh)?\s*\n?)?\b(file_read|file_write|file_edit|shell|skill_execute|skill_list|skill_build|grep_search|find_files|web_search|web_fetch|task_complete|memory_read|memory_write|list_directory|ask_user)\b[\s(=\-]/;
19451
19452
  const narratedMatch = content.match(narratedToolPattern);
19452
19453
  if (narratedMatch) {
19453
19454
  narratedToolCallCount++;
@@ -27119,6 +27120,12 @@ function formatToolArgs(toolName, args, verbose) {
27119
27120
  return `${args["path"] ?? ""}${args["model"] ? ` (${args["model"]})` : ""}`;
27120
27121
  case "transcribe_url":
27121
27122
  return truncStr(String(args["url"] ?? ""), maxArg);
27123
+ case "ask_user": {
27124
+ const q = truncStr(String(args["question"] ?? ""), maxArg - 10);
27125
+ const opts = args["options"];
27126
+ const count = opts ? ` (${opts.length} options)` : "";
27127
+ return `${q}${c2.dim(count)}`;
27128
+ }
27122
27129
  default:
27123
27130
  return Object.entries(args).map(([k, v]) => `${k}=${truncStr(String(v), Math.max(30, maxArg / 3))}`).join(", ");
27124
27131
  }
@@ -27221,7 +27228,9 @@ var init_render = __esm({
27221
27228
  ocr: "\u{1F441}\uFE0F",
27222
27229
  // Transcription tools
27223
27230
  transcribe_file: "\u{1F399}\uFE0F",
27224
- transcribe_url: "\u{1F399}\uFE0F"
27231
+ transcribe_url: "\u{1F399}\uFE0F",
27232
+ // User interaction
27233
+ ask_user: "\u2753"
27225
27234
  };
27226
27235
  TOOL_LABELS = {
27227
27236
  file_read: "Read",
@@ -27255,7 +27264,9 @@ var init_render = __esm({
27255
27264
  ocr: "OCR",
27256
27265
  // Transcription tools
27257
27266
  transcribe_file: "Transcribe",
27258
- transcribe_url: "Transcribe URL"
27267
+ transcribe_url: "Transcribe URL",
27268
+ // User interaction
27269
+ ask_user: "Ask user"
27259
27270
  };
27260
27271
  TOOL_COLORS = {
27261
27272
  file_read: pastel.sky,
@@ -27286,7 +27297,8 @@ var init_render = __esm({
27286
27297
  screenshot: pastel.pink,
27287
27298
  ocr: pastel.pink,
27288
27299
  transcribe_file: pastel.peach,
27289
- transcribe_url: pastel.peach
27300
+ transcribe_url: pastel.peach,
27301
+ ask_user: pastel.sky
27290
27302
  };
27291
27303
  _contentWriteHook = null;
27292
27304
  HINTS = [
@@ -27337,6 +27349,7 @@ var init_render = __esm({
27337
27349
  "aiwg_workflow",
27338
27350
  "create_tool",
27339
27351
  "manage_tools",
27352
+ "ask_user",
27340
27353
  "task_complete"
27341
27354
  ];
27342
27355
  COMMAND_NAMES = [
@@ -46653,7 +46666,7 @@ function formatDMNToolArgs(toolName, args) {
46653
46666
  return "";
46654
46667
  }
46655
46668
  }
46656
- function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled) {
46669
+ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled, slashCommandHandler, thinkingEnabled, askUserCallback) {
46657
46670
  const voiceStyleMap = {
46658
46671
  concise: 1,
46659
46672
  balanced: 3,
@@ -46924,6 +46937,62 @@ ${entry.fullContent}`
46924
46937
  }
46925
46938
  });
46926
46939
  }
46940
+ if (askUserCallback) {
46941
+ runner.registerTool({
46942
+ name: "ask_user",
46943
+ description: `Present a decision point to the user with selectable options via an interactive TUI picker. Use this when you need user input to proceed \u2014 choosing between approaches, confirming destructive actions, selecting from a list of alternatives, or any situation where the user's preference matters. For yes/no questions, pass options: ["Yes", "No"]. For multi-choice, pass all available options. The user navigates with arrow keys and selects with Enter. The tool blocks until the user responds.`,
46944
+ parameters: {
46945
+ type: "object",
46946
+ properties: {
46947
+ question: {
46948
+ type: "string",
46949
+ description: "The question or decision to present to the user. Be clear and concise."
46950
+ },
46951
+ options: {
46952
+ type: "array",
46953
+ items: { type: "string" },
46954
+ description: 'Array of selectable options. For binary: ["Yes", "No"]. For multi-choice: ["Option A", "Option B", ...].'
46955
+ },
46956
+ allow_multiple: {
46957
+ type: "boolean",
46958
+ description: "If true, user can select multiple options (space to toggle, Enter to confirm). Default: false."
46959
+ }
46960
+ },
46961
+ required: ["question", "options"]
46962
+ },
46963
+ async execute(args) {
46964
+ const question = String(args.question ?? "");
46965
+ const rawOptions = args.options;
46966
+ const allowMultiple = Boolean(args.allow_multiple);
46967
+ if (!question) {
46968
+ return { success: false, output: "", error: "question is required" };
46969
+ }
46970
+ if (!Array.isArray(rawOptions) || rawOptions.length === 0) {
46971
+ return { success: false, output: "", error: "options must be a non-empty array of strings" };
46972
+ }
46973
+ const options = rawOptions.map((o) => String(o));
46974
+ if (options.length > 20) {
46975
+ return { success: false, output: "", error: "Too many options (max 20). Reduce the choices." };
46976
+ }
46977
+ try {
46978
+ const selected = await askUserCallback(question, options, allowMultiple);
46979
+ if (selected.length === 0) {
46980
+ return { success: true, output: "User cancelled the selection (pressed Escape)." };
46981
+ }
46982
+ return {
46983
+ success: true,
46984
+ output: allowMultiple ? `User selected: ${selected.join(", ")}` : `User selected: ${selected[0]}`
46985
+ };
46986
+ } catch (err) {
46987
+ return {
46988
+ success: false,
46989
+ output: "",
46990
+ error: `Ask user failed: ${err instanceof Error ? err.message : String(err)}`
46991
+ };
46992
+ }
46993
+ }
46994
+ });
46995
+ }
46927
46996
  const filesTouched = /* @__PURE__ */ new Set();
46928
46997
  const toolSequence = [];
46929
46998
  const editSessionId = `task-${Date.now()}`;
@@ -47807,6 +47876,65 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
47807
47876
  const sudoPromptStr = ` ${c2.bold(c2.yellow("\u{1F511} Password:"))} `;
47808
47877
  process.stdout.write(sudoPromptStr);
47809
47878
  }
47879
+ async function handleAskUser(question, options, allowMultiple) {
47880
+ if (statusBar?.isActive)
47881
+ statusBar.beginContentWrite();
47882
+ writeContent(() => {
47883
+ process.stdout.write(`
47884
+ ${c2.bold(c2.cyan("?"))} ${c2.bold(question)}
47885
+ `);
47886
+ });
47887
+ const items = options.map((opt, i) => ({
47888
+ key: String(i),
47889
+ label: opt
47890
+ }));
47891
+ if (allowMultiple) {
47892
+ const selected = /* @__PURE__ */ new Set();
47893
+ const result = await tuiSelect({
47894
+ items,
47895
+ title: "Select one or more (Space to toggle, Enter to confirm)",
47896
+ rl,
47897
+ renderRow: (item, focused, _isActive) => {
47898
+ const isSelected = selected.has(item.key);
47899
+ const marker = isSelected ? c2.green("\u2611") : focused ? c2.cyan("\u2610") : c2.dim("\u2610");
47900
+ const label = focused ? c2.bold(c2.cyan(item.label)) : isSelected ? c2.green(item.label) : item.label;
47901
+ return ` ${marker} ${label}`;
47902
+ },
47903
+ onAction: (item, action) => {
47904
+ if (action === "space") {
47905
+ if (selected.has(item.key)) {
47906
+ selected.delete(item.key);
47907
+ } else {
47908
+ selected.add(item.key);
47909
+ }
47910
+ return true;
47911
+ }
47912
+ return false;
47913
+ }
47914
+ });
47915
+ if (statusBar?.isActive)
47916
+ statusBar.endContentWrite();
47917
+ if (!result.confirmed)
47918
+ return [];
47919
+ if (selected.size === 0 && result.key !== null) {
47920
+ const idx = parseInt(result.key, 10);
47921
+ return [options[idx]];
47922
+ }
47923
+ return Array.from(selected).map((k) => options[parseInt(k, 10)]);
47924
+ } else {
47925
+ const result = await tuiSelect({
47926
+ items,
47927
+ title: "Select one option",
47928
+ rl
47929
+ });
47930
+ if (statusBar?.isActive)
47931
+ statusBar.endContentWrite();
47932
+ if (!result.confirmed || result.key === null)
47933
+ return [];
47934
+ const idx = parseInt(result.key, 10);
47935
+ return [options[idx]];
47936
+ }
47937
+ }
47810
47938
  const evalBackend = {
47811
47939
  async evaluate(prompt) {
47812
47940
  const backend = new OllamaAgenticBackend(currentConfig.backendUrl, currentConfig.model, currentConfig.apiKey);
@@ -49072,7 +49200,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
49072
49200
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary, meta) => {
49073
49201
  lastCompletedSummary = summary;
49074
49202
  lastTaskMeta = meta ?? null;
49075
- }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled, buildSlashCommandHandler(), thinkingEnabled);
49203
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled, buildSlashCommandHandler(), thinkingEnabled, handleAskUser);
49076
49204
  activeTask = task;
49077
49205
  showPrompt();
49078
49206
  await task.promise;
@@ -49304,7 +49432,7 @@ NEW TASK: ${fullInput}`;
49304
49432
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary, meta) => {
49305
49433
  lastCompletedSummary = summary;
49306
49434
  lastTaskMeta = meta ?? null;
49307
- }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled, buildSlashCommandHandler(), thinkingEnabled);
49435
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled, buildSlashCommandHandler(), thinkingEnabled, handleAskUser);
49308
49436
  activeTask = task;
49309
49437
  showPrompt();
49310
49438
  await task.promise;
@@ -49574,6 +49702,7 @@ var init_interactive = __esm({
49574
49702
  init_carousel_descriptors();
49575
49703
  init_voice();
49576
49704
  init_stream_renderer();
49705
+ init_tui_select();
49577
49706
  init_edit_history();
49578
49707
  init_dream_engine();
49579
49708
  init_bless_engine();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.103.75",
3
+ "version": "0.103.76",
4
4
  "description": "AI coding agent powered by open-source models (Ollama/vLLM) — interactive TUI with agentic tool-calling loop",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",