open-agents-ai 0.187.290 → 0.187.292

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 +237 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -270036,6 +270036,41 @@ ${graphSummary}`,
270036
270036
  * the entire messages array. Reset on run(). */
270037
270037
  _lastTodoWriteTurn = -1;
270038
270038
  _lastTodoReminderTurn = -1;
270039
+ /**
270040
+ * Check whether the current session's todo list (if any) has any open items.
270041
+ * Returns the list of open items (pending/in_progress/blocked). If there is
270042
+ * no todo list, returns an empty array to allow task completion.
270043
+ */
270044
+ getOpenTodoItems() {
270045
+ const todos = this.readSessionTodos();
270046
+ if (!todos || todos.length === 0)
270047
+ return [];
270048
+ return todos.filter((t2) => t2.status !== "completed");
270049
+ }
270050
+ /**
270051
+ * Build a guard prompt injected when the model attempts to call task_complete
270052
+ * while there are open todo items. This asks the model to verify each item by
270053
+ * name with objective evidence, complete remaining items in order, update the
270054
+ * checklist via todo_write, and only then call task_complete.
270055
+ */
270056
+ buildTodoCompletionGuard(openItems) {
270057
+ const list = openItems.slice(0, 20).map((t2, i2) => `${i2 + 1}. [${t2.status}] ${t2.content}`).join("\n");
270058
+ return `[TODO COMPLETION REQUIRED]
270059
+ You attempted to call task_complete, but the session checklist still has ${openItems.length} open item(s).
270060
+ Before finishing, do ALL of the following:
270061
+ 1) For each item BY NAME, state if it is completed and provide objective evidence (file diffs, test output, logs, or concrete code references).
270062
+ 2) If any item is not completed, proceed to complete them IN ORDER.
270063
+ 3) After all items are truly implemented, call todo_write to mark every item as "completed".
270064
+ 4) ONLY AFTER updating the todo list, call task_complete with a concise final summary.
270065
+
270066
+ Current checklist (open items):
270067
+ ${list}
270068
+
270069
+ Respond with a short verification section in this shape (example):
270070
+ - verify: [{ name: "<exact item text>", completed: true|false, evidence: "<objective proof>" }, ...]
270071
+ - next: "what you will do next OR the exact todo_write(...) call to update statuses"
270072
+ Do NOT call task_complete until all items are marked completed via todo_write.`;
270073
+ }
270039
270074
  /**
270040
270075
  * WO-META-TRACK — Hannover-style turn-counter reminder.
270041
270076
  *
@@ -270243,6 +270278,40 @@ ${body}`;
270243
270278
  this.registerTool(tool);
270244
270279
  }
270245
270280
  }
270281
+ /**
270282
+ * Get a static catalog of registered tools for discovery/minified prompts.
270283
+ * Only exposes name, description, and parameters schema (JSON shape).
270284
+ */
270285
+ getToolCatalog() {
270286
+ const list = [];
270287
+ for (const t2 of this.tools.values()) {
270288
+ list.push({ name: t2.name, description: t2.description, parameters: t2.parameters });
270289
+ }
270290
+ return list;
270291
+ }
270292
+ /**
270293
+ * Execute a registered tool by name with provided args.
270294
+ * Validates against inputSchema if present and returns the tool result.
270295
+ */
270296
+ async runToolByName(name11, args2) {
270297
+ const tool = this.tools.get(name11);
270298
+ if (!tool) {
270299
+ return { success: false, output: "", error: `Unknown tool: ${name11}` };
270300
+ }
270301
+ try {
270302
+ if (tool.inputSchema) {
270303
+ tool.inputSchema.parse(args2);
270304
+ }
270305
+ } catch (e2) {
270306
+ return { success: false, output: "", error: `Invalid args for ${name11}: ${e2?.message || String(e2)}` };
270307
+ }
270308
+ try {
270309
+ const result = await tool.execute(args2);
270310
+ return result;
270311
+ } catch (e2) {
270312
+ return { success: false, output: "", error: e2?.message || String(e2) };
270313
+ }
270314
+ }
270246
270315
  /** Subscribe to agent events */
270247
270316
  onEvent(handler) {
270248
270317
  this.handlers.push(handler);
@@ -271677,13 +271746,24 @@ Then use file_read on individual FILES inside it.`);
271677
271746
  ${sr.result.output}`;
271678
271747
  messages2.push(this.buildToolMessage(output, matchTc.id));
271679
271748
  if (matchTc.name === "task_complete") {
271680
- completed = true;
271681
- summary = extractTaskCompleteSummary(matchTc.arguments);
271682
- if (summary && !this._assistantTextEmitted) {
271683
- this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271684
- this._assistantTextEmitted = true;
271749
+ const open2 = this.getOpenTodoItems();
271750
+ if (open2.length > 0) {
271751
+ const guard = this.buildTodoCompletionGuard(open2);
271752
+ messages2.push({ role: "system", content: guard });
271753
+ this.emit({
271754
+ type: "status",
271755
+ content: `task_complete intercepted — ${open2.length} open todo(s) remain`,
271756
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
271757
+ });
271758
+ } else {
271759
+ completed = true;
271760
+ summary = extractTaskCompleteSummary(matchTc.arguments);
271761
+ if (summary && !this._assistantTextEmitted) {
271762
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271763
+ this._assistantTextEmitted = true;
271764
+ }
271765
+ break;
271685
271766
  }
271686
- break;
271687
271767
  }
271688
271768
  }
271689
271769
  }
@@ -271696,13 +271776,24 @@ ${sr.result.output}`;
271696
271776
  if (r2) {
271697
271777
  messages2.push(this.buildToolMessage(r2.output, r2.tc.id));
271698
271778
  if (r2.tc.name === "task_complete") {
271699
- completed = true;
271700
- summary = extractTaskCompleteSummary(r2.tc.arguments);
271701
- if (summary && !this._assistantTextEmitted) {
271702
- this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271703
- this._assistantTextEmitted = true;
271779
+ const open2 = this.getOpenTodoItems();
271780
+ if (open2.length > 0) {
271781
+ const guard = this.buildTodoCompletionGuard(open2);
271782
+ messages2.push({ role: "system", content: guard });
271783
+ this.emit({
271784
+ type: "status",
271785
+ content: `task_complete intercepted — ${open2.length} open todo(s) remain`,
271786
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
271787
+ });
271788
+ } else {
271789
+ completed = true;
271790
+ summary = extractTaskCompleteSummary(r2.tc.arguments);
271791
+ if (summary && !this._assistantTextEmitted) {
271792
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271793
+ this._assistantTextEmitted = true;
271794
+ }
271795
+ break;
271704
271796
  }
271705
- break;
271706
271797
  }
271707
271798
  }
271708
271799
  }
@@ -271725,13 +271816,24 @@ ${sr.result.output}`;
271725
271816
  if (r2) {
271726
271817
  messages2.push(this.buildToolMessage(r2.output, r2.tc.id));
271727
271818
  if (r2.tc.name === "task_complete") {
271728
- completed = true;
271729
- summary = extractTaskCompleteSummary(r2.tc.arguments);
271730
- if (summary && !this._assistantTextEmitted) {
271731
- this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271732
- this._assistantTextEmitted = true;
271819
+ const open2 = this.getOpenTodoItems();
271820
+ if (open2.length > 0) {
271821
+ const guard = this.buildTodoCompletionGuard(open2);
271822
+ messages2.push({ role: "system", content: guard });
271823
+ this.emit({
271824
+ type: "status",
271825
+ content: `task_complete intercepted — ${open2.length} open todo(s) remain`,
271826
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
271827
+ });
271828
+ } else {
271829
+ completed = true;
271830
+ summary = extractTaskCompleteSummary(r2.tc.arguments);
271831
+ if (summary && !this._assistantTextEmitted) {
271832
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
271833
+ this._assistantTextEmitted = true;
271834
+ }
271835
+ break;
271733
271836
  }
271734
- break;
271735
271837
  }
271736
271838
  }
271737
271839
  }
@@ -272149,13 +272251,24 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
272149
272251
  const toolMsg = this.buildToolMessage(output, tc.id);
272150
272252
  messages2.push(toolMsg);
272151
272253
  if (tc.name === "task_complete") {
272152
- completed = true;
272153
- summary = extractTaskCompleteSummary(tc.arguments);
272154
- if (summary && !this._assistantTextEmitted) {
272155
- this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
272156
- this._assistantTextEmitted = true;
272254
+ const open2 = this.getOpenTodoItems();
272255
+ if (open2.length > 0) {
272256
+ const guard = this.buildTodoCompletionGuard(open2);
272257
+ messages2.push({ role: "system", content: guard });
272258
+ this.emit({
272259
+ type: "status",
272260
+ content: `task_complete intercepted — ${open2.length} open todo(s) remain`,
272261
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
272262
+ });
272263
+ } else {
272264
+ completed = true;
272265
+ summary = extractTaskCompleteSummary(tc.arguments);
272266
+ if (summary && !this._assistantTextEmitted) {
272267
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
272268
+ this._assistantTextEmitted = true;
272269
+ }
272270
+ break;
272157
272271
  }
272158
- break;
272159
272272
  }
272160
272273
  }
272161
272274
  if (completed)
@@ -272172,9 +272285,20 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
272172
272285
  }
272173
272286
  this.emit({ type: "model_response", content: content.slice(0, 200), turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
272174
272287
  if (/task.?complete|all tests pass/i.test(content)) {
272175
- completed = true;
272176
- summary = content;
272177
- break;
272288
+ const open2 = this.getOpenTodoItems();
272289
+ if (open2.length > 0) {
272290
+ const guard = this.buildTodoCompletionGuard(open2);
272291
+ messages2.push({ role: "system", content: guard });
272292
+ this.emit({
272293
+ type: "status",
272294
+ content: `task_complete text detected but intercepted — ${open2.length} open todo(s) remain`,
272295
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
272296
+ });
272297
+ } else {
272298
+ completed = true;
272299
+ summary = content;
272300
+ break;
272301
+ }
272178
272302
  }
272179
272303
  const finalVarMatch = content.match(/FINAL_VAR\s*\(\s*["']?(\w+)["']?\s*\)/);
272180
272304
  if (finalVarMatch && this.options.finalVarResolver) {
@@ -284920,6 +285044,42 @@ function truncate2(s2, max) {
284920
285044
  if (s2.length <= max) return s2.padEnd(max, " ");
284921
285045
  return s2.slice(0, Math.max(0, max - 1)) + "…";
284922
285046
  }
285047
+ function stripAnsi2(s2) {
285048
+ return s2.replace(/\x1B\[[0-9;]*[A-Za-z]/g, "").replace(/\x1B\][^\x07]*\x07/g, "").replace(/\x1B[()][A-Z0-9]/g, "").replace(/\x1B\[?\??[0-9;]*[a-zA-Z]/g, "").replace(/\x1B/g, "");
285049
+ }
285050
+ function visualLen(s2) {
285051
+ return stripAnsi2(s2).length;
285052
+ }
285053
+ function buildTodoProgressBar(todos, maxWidth) {
285054
+ if (maxWidth <= 0 || todos.length === 0) return "";
285055
+ let cells = Math.min(todos.length, maxWidth);
285056
+ const truncated = cells < todos.length;
285057
+ const inIdx = todos.findIndex((t2) => t2.status === "in_progress");
285058
+ let nextIdx = -1;
285059
+ if (inIdx >= 0) {
285060
+ nextIdx = todos.findIndex((t2, i2) => i2 > inIdx && t2.status !== "completed");
285061
+ }
285062
+ if (nextIdx < 0) nextIdx = todos.findIndex((t2) => t2.status === "pending");
285063
+ const PEND = DIM_LABEL;
285064
+ const INPROG = ACCENT;
285065
+ const NEXT = ACCENT;
285066
+ const DONE_Y = ACCENT;
285067
+ let out = "";
285068
+ for (let i2 = 0; i2 < cells; i2++) {
285069
+ const t2 = todos[i2];
285070
+ if (t2.status === "completed") {
285071
+ out += `\x1B[1m${DONE_Y}█${RESET}`;
285072
+ } else if (i2 === inIdx) {
285073
+ out += `${INPROG}▒${RESET}`;
285074
+ } else if (i2 === nextIdx && inIdx >= 0) {
285075
+ out += `${NEXT}▒${RESET}`;
285076
+ } else {
285077
+ out += `${PEND}░${RESET}`;
285078
+ }
285079
+ }
285080
+ if (truncated && maxWidth > 0) out += `${DIM_LABEL}…${RESET}`;
285081
+ return out;
285082
+ }
284923
285083
  function render() {
284924
285084
  if (!_enabled) return;
284925
285085
  if (!panelEffectivelyVisible()) {
@@ -284939,7 +285099,11 @@ function render() {
284939
285099
  const total = _lastTodos.length;
284940
285100
  const headerColor = ACCENT;
284941
285101
  const lines = [];
284942
- const headerText = `tasks ${headerColor}${completed}/${total}${RESET}${DIM_LABEL} (todo_write)${RESET}`;
285102
+ const headerPrefix = `tasks ${headerColor}${completed}/${total}${RESET} `;
285103
+ const headerPrefixWidth = visualLen(headerPrefix);
285104
+ const maxBarWidth = Math.max(0, cols - 2 - headerPrefixWidth);
285105
+ const progressBar = buildTodoProgressBar(_lastTodos, maxBarWidth);
285106
+ const headerText = `${headerPrefix}${progressBar}`;
284943
285107
  lines.push(headerText);
284944
285108
  const visible = _lastTodos.slice(0, MAX_VISIBLE_ROWS - 1);
284945
285109
  for (const t2 of visible) {
@@ -285447,7 +285611,7 @@ var init_status_bar = __esm({
285447
285611
  return `\x1B]8;;${cmdPrefix}\x07\x1B[38;5;${fg2}m${label}\x1B]8;;\x07`;
285448
285612
  };
285449
285613
  const verBase = ` OA v${this._version}`;
285450
- const verArrow = this._updateLatest ? " " : "";
285614
+ const verArrow = this._updateLatest ? " 🢁" : "";
285451
285615
  const verText = verBase + verArrow;
285452
285616
  const menuBtns = [
285453
285617
  { cmd: "help", label: " help ", w: 6 },
@@ -285477,11 +285641,11 @@ var init_status_bar = __esm({
285477
285641
  let out = "";
285478
285642
  let usedW = 0;
285479
285643
  if (isFirstPage) {
285480
- if (this._updateLatest) {
285481
- out += `\x1B]8;;oa-cmd:/update\x07\x1B[1;38;5;${TEXT_PRIMARY}m${verText}\x1B]8;;\x07\x1B[0m`;
285482
- } else {
285483
- out += `\x1B[1;38;5;${TEXT_PRIMARY}m${verText}\x1B[0m`;
285644
+ try {
285645
+ this._verClickZone = this._updateLatest ? { start: 4, end: 4 + verW - 1 } : null;
285646
+ } catch {
285484
285647
  }
285648
+ out += `\x1B[1;38;5;${TEXT_PRIMARY}m${verText}\x1B[0m`;
285485
285649
  usedW += verW;
285486
285650
  }
285487
285651
  let btnTotalW = 0;
@@ -286432,6 +286596,14 @@ var init_status_bar = __esm({
286432
286596
  return;
286433
286597
  }
286434
286598
  }
286599
+ if (type === "press" && this._updateLatest) {
286600
+ const hdrRow2 = layout().headerContent;
286601
+ const verZone = this._verClickZone;
286602
+ if (row === hdrRow2 && verZone && col >= verZone.start && col <= verZone.end) {
286603
+ if (this._headerButtonHandler) this._headerButtonHandler("/update");
286604
+ return;
286605
+ }
286606
+ }
286435
286607
  const cmd = hitTestHeaderButton(row, col, w);
286436
286608
  if (type === "press" && cmd) {
286437
286609
  if (cmd === "header-prev") {
@@ -287960,7 +288132,7 @@ function ansi2(code8, text) {
287960
288132
  function fg2562(code8, text) {
287961
288133
  return isTTY2 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
287962
288134
  }
287963
- function stripAnsi2(s2) {
288135
+ function stripAnsi3(s2) {
287964
288136
  return s2.replace(/\x1B\[[0-9;]*m/g, "");
287965
288137
  }
287966
288138
  function defaultRenderRow(item, focused, isActive) {
@@ -287974,8 +288146,8 @@ function matchRow(item, focused, isActive) {
287974
288146
  return defaultRenderRow(item, focused, isActive);
287975
288147
  }
287976
288148
  const marker = selectColors.matchLight("○");
287977
- const label = selectColors.matchLight(stripAnsi2(item.label));
287978
- const detail = item.detail ? ` ${selectColors.dim(stripAnsi2(item.detail))}` : "";
288149
+ const label = selectColors.matchLight(stripAnsi3(item.label));
288150
+ const detail = item.detail ? ` ${selectColors.dim(stripAnsi3(item.detail))}` : "";
287979
288151
  return ` ${marker} ${label}${detail}`;
287980
288152
  }
287981
288153
  function tuiSelect(opts) {
@@ -287999,8 +288171,8 @@ function tuiSelect(opts) {
287999
288171
  matchSet = /* @__PURE__ */ new Set();
288000
288172
  for (let i2 = 0; i2 < items.length; i2++) {
288001
288173
  if (isSkippable(i2)) continue;
288002
- const plain = stripAnsi2(items[i2].label).toLowerCase();
288003
- const detailPlain = items[i2].detail ? stripAnsi2(items[i2].detail).toLowerCase() : "";
288174
+ const plain = stripAnsi3(items[i2].label).toLowerCase();
288175
+ const detailPlain = items[i2].detail ? stripAnsi3(items[i2].detail).toLowerCase() : "";
288004
288176
  if (plain.includes(lower) || detailPlain.includes(lower)) {
288005
288177
  matchSet.add(i2);
288006
288178
  }
@@ -288125,7 +288297,7 @@ function tuiSelect(opts) {
288125
288297
  if (deleteConfirmIdx === idx) {
288126
288298
  const yesLabel = deleteConfirmSel ? selectColors.bold(selectColors.green("[Yes]")) : selectColors.dim("[Yes]");
288127
288299
  const noLabel = !deleteConfirmSel ? selectColors.bold(selectColors.orange("[No]")) : selectColors.dim("[No]");
288128
- lines.push(` ${ansi2("31", "✕")} ${ansi2("31", stripAnsi2(item.label))} Delete? ${yesLabel} ${noLabel}`);
288300
+ lines.push(` ${ansi2("31", "✕")} ${ansi2("31", stripAnsi3(item.label))} Delete? ${yesLabel} ${noLabel}`);
288129
288301
  } else if (filter2) {
288130
288302
  lines.push(matchRow(item, focused, isActive));
288131
288303
  } else {
@@ -291735,11 +291907,11 @@ import { extname as extname10, resolve as resolve32 } from "node:path";
291735
291907
  function ansi3(code8, text) {
291736
291908
  return isTTY3 ? `\x1B[${code8}m${text}\x1B[0m` : text;
291737
291909
  }
291738
- function stripAnsi3(s2) {
291910
+ function stripAnsi4(s2) {
291739
291911
  return s2.replace(/\x1B\[[0-9;]*m/g, "");
291740
291912
  }
291741
291913
  function fitToWidth(text, width) {
291742
- const visible = stripAnsi3(text);
291914
+ const visible = stripAnsi4(text);
291743
291915
  if (visible.length >= width) {
291744
291916
  let visCount = 0;
291745
291917
  let i2 = 0;
@@ -331550,32 +331722,31 @@ ${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
331550
331722
  setupTasks.push(visionDepsPromise.catch(() => {
331551
331723
  }));
331552
331724
  let updateNotified = false;
331553
- if (!isResumed) {
331554
- checkForUpdate(version4).then((updateInfo) => {
331555
- if (updateInfo) {
331556
- banner.setUpdateAvailable(updateInfo.latestVersion);
331557
- try {
331558
- statusBar.setUpdateAvailable(updateInfo.latestVersion);
331559
- } catch {
331560
- }
331561
- const vTextLen = ` OA v${version4}`.length;
331562
- const { setUpdateBadgeRegion: setUpdateBadgeRegion2 } = (init_text_selection(), __toCommonJS(text_selection_exports));
331563
- setUpdateBadgeRegion2(true, vTextLen + 1, " UPDATE ".length);
331564
- const writeMsg = () => {
331565
- if (statusBar?.isActive && statusBar.isStreaming) {
331566
- setTimeout(writeMsg, 3e3);
331567
- return;
331568
- }
331569
- updateNotified = true;
331570
- if (statusBar?.isActive) statusBar.beginContentWrite();
331571
- renderInfo2(`Update available: v${updateInfo.currentVersion} → v${c3.bold(c3.green(updateInfo.latestVersion))}. Click UPDATE in header or run /update.`);
331572
- if (statusBar?.isActive) statusBar.endContentWrite();
331573
- };
331574
- writeMsg();
331725
+ checkForUpdate(version4).then((updateInfo) => {
331726
+ if (updateInfo) {
331727
+ banner.setUpdateAvailable(updateInfo.latestVersion);
331728
+ try {
331729
+ statusBar.setUpdateAvailable(updateInfo.latestVersion);
331730
+ } catch {
331575
331731
  }
331576
- }).catch(() => {
331577
- });
331578
- }
331732
+ const vTextLen = ` OA v${version4}`.length;
331733
+ const { setUpdateBadgeRegion: setUpdateBadgeRegion2 } = (init_text_selection(), __toCommonJS(text_selection_exports));
331734
+ setUpdateBadgeRegion2(true, vTextLen + 1, " UPDATE ".length);
331735
+ const writeMsg = () => {
331736
+ if (updateNotified) return;
331737
+ if (statusBar?.isActive && statusBar.isStreaming) {
331738
+ setTimeout(writeMsg, 3e3);
331739
+ return;
331740
+ }
331741
+ updateNotified = true;
331742
+ if (statusBar?.isActive) statusBar.beginContentWrite();
331743
+ renderInfo2(`Update available: v${updateInfo.currentVersion} → v${c3.bold(c3.green(updateInfo.latestVersion))}. Click version ↑ or run /update.`);
331744
+ if (statusBar?.isActive) statusBar.endContentWrite();
331745
+ };
331746
+ if (!isResumed) writeMsg();
331747
+ }
331748
+ }).catch(() => {
331749
+ });
331579
331750
  const AUTO_UPDATE_INTERVAL_MS = 30 * 60 * 1e3;
331580
331751
  const autoUpdateTimer = setInterval(() => {
331581
331752
  if (updateNotified) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.290",
3
+ "version": "0.187.292",
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",