open-agents-ai 0.187.206 → 0.187.207

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 +100 -18
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -268905,6 +268905,36 @@ function getSystemPromptForTier(tier) {
268905
268905
  return SYSTEM_PROMPT;
268906
268906
  }
268907
268907
  }
268908
+ function computeTodoReminder(input) {
268909
+ const turnsSinceWriteThreshold = input.turnsSinceWriteThreshold ?? 10;
268910
+ const turnsBetweenReminders = input.turnsBetweenReminders ?? 10;
268911
+ if (!input.todos || input.todos.length === 0) {
268912
+ return { shouldInject: false, content: null, reason: "no_todos" };
268913
+ }
268914
+ if (input.lastTodoWriteTurn >= 0) {
268915
+ const turnsSinceWrite = input.currentTurn - input.lastTodoWriteTurn;
268916
+ if (turnsSinceWrite < turnsSinceWriteThreshold) {
268917
+ return { shouldInject: false, content: null, reason: "recent_write" };
268918
+ }
268919
+ } else if (input.currentTurn < turnsSinceWriteThreshold) {
268920
+ return { shouldInject: false, content: null, reason: "too_early" };
268921
+ }
268922
+ if (input.lastReminderTurn >= 0) {
268923
+ const turnsSinceReminder = input.currentTurn - input.lastReminderTurn;
268924
+ if (turnsSinceReminder < turnsBetweenReminders) {
268925
+ return { shouldInject: false, content: null, reason: "recent_reminder" };
268926
+ }
268927
+ }
268928
+ const todoItems = input.todos.slice(0, 12).map((t2, i2) => `${i2 + 1}. [${t2.status}] ${t2.content}`).join("\n");
268929
+ const content = `<system-reminder>
268930
+ The todo_write tool hasn't been used recently. If you're working on tasks that would benefit from tracking progress, consider using todo_write to track your progress. Mark the current task in_progress and future tasks pending. When you complete a task, mark it completed immediately \u2014 don't batch completions. Also consider cleaning up the todo list if it has become stale and no longer matches what you are working on. Only use it if it's relevant to the current work. This is just a gentle reminder \u2014 ignore if not applicable. Make sure that you NEVER mention this reminder to the user.
268931
+
268932
+ Here are the existing contents of your todo list:
268933
+
268934
+ ${todoItems}
268935
+ </system-reminder>`;
268936
+ return { shouldInject: true, content, reason: "injected" };
268937
+ }
268908
268938
  var SYSTEM_PROMPT, SYSTEM_PROMPT_MEDIUM, SYSTEM_PROMPT_SMALL, AgenticRunner, OllamaAgenticBackend;
268909
268939
  var init_agenticRunner = __esm({
268910
268940
  "packages/orchestrator/dist/agenticRunner.js"() {
@@ -269227,30 +269257,65 @@ ${graphSummary}`,
269227
269257
  *
269228
269258
  * Max ~300 tokens to avoid context bloat.
269229
269259
  */
269230
- buildPlanSkeleton() {
269231
- const ts = this._taskState;
269232
- const parts = [];
269260
+ /**
269261
+ * WO-META-TRACK Read the authoritative todo list from disk.
269262
+ * Returns null if no session id OR no file. Used by buildPlanSkeleton
269263
+ * and by the turn-counter reminder.
269264
+ */
269265
+ readSessionTodos() {
269233
269266
  try {
269234
269267
  const sid = process.env["OA_SESSION_ID"] || this._sessionId || "default";
269235
269268
  const safe = sid.replace(/[^a-zA-Z0-9_.-]/g, "_");
269236
269269
  const fp = _pathJoin(_osHomedir(), ".open-agents", "todos", `${safe}.json`);
269237
- if (_fsExistsSync(fp)) {
269238
- const todos = JSON.parse(_fsReadFileSync(fp, "utf-8"));
269239
- if (Array.isArray(todos) && todos.length > 0) {
269240
- const lines = todos.slice(0, 12).map((t2) => {
269241
- const mark = t2.status === "completed" ? "[x]" : t2.status === "in_progress" ? "[~]" : t2.status === "blocked" ? "[!]" : "[ ]";
269242
- const blocker = t2.blocker ? ` (blocked: ${t2.blocker.slice(0, 40)})` : "";
269243
- return `${mark} ${t2.content.slice(0, 80)}${blocker}`;
269244
- });
269245
- const completedN = todos.filter((t2) => t2.status === "completed").length;
269246
- const totalN = todos.length;
269247
- parts.push(`## CURRENT TODO LIST (${completedN}/${totalN} complete)
269248
- ` + lines.join("\n") + `
269249
-
269250
- Continue with the current in_progress item. When you finish it, call todo_write to mark it completed and the next one in_progress.`);
269270
+ if (!_fsExistsSync(fp))
269271
+ return null;
269272
+ const parsed = JSON.parse(_fsReadFileSync(fp, "utf-8"));
269273
+ return Array.isArray(parsed) ? parsed : null;
269274
+ } catch {
269275
+ return null;
269276
+ }
269277
+ }
269278
+ /** Track the turn index of the last todo_write call so the reminder
269279
+ * path can compute `turnsSinceLastTodoWrite` cheaply without walking
269280
+ * the entire messages array. Reset on run(). */
269281
+ _lastTodoWriteTurn = -1;
269282
+ _lastTodoReminderTurn = -1;
269283
+ /**
269284
+ * WO-META-TRACK — Hannover-style turn-counter reminder.
269285
+ *
269286
+ * Delegates to the pure `computeTodoReminder` function so the gating
269287
+ * logic is independently testable without instantiating a backend.
269288
+ * Mutates `_lastTodoReminderTurn` when a reminder is produced.
269289
+ */
269290
+ getTodoReminderContent(currentTurn) {
269291
+ const todos = this.readSessionTodos();
269292
+ const result = computeTodoReminder({
269293
+ currentTurn,
269294
+ lastTodoWriteTurn: this._lastTodoWriteTurn,
269295
+ lastReminderTurn: this._lastTodoReminderTurn,
269296
+ todos
269297
+ });
269298
+ if (result.shouldInject) {
269299
+ this._lastTodoReminderTurn = currentTurn;
269300
+ return result.content;
269301
+ }
269302
+ return null;
269303
+ }
269304
+ buildPlanSkeleton() {
269305
+ const ts = this._taskState;
269306
+ const parts = [];
269307
+ const todos = this.readSessionTodos();
269308
+ if (todos && todos.length > 0) {
269309
+ const current = todos.find((t2) => t2.status === "in_progress");
269310
+ const completedN = todos.filter((t2) => t2.status === "completed").length;
269311
+ if (current) {
269312
+ parts.push(`[plan: ${completedN}/${todos.length} complete \xB7 currently: ${current.content.slice(0, 80)}]`);
269313
+ } else {
269314
+ const nextPending = todos.find((t2) => t2.status === "pending");
269315
+ if (nextPending) {
269316
+ parts.push(`[plan: ${completedN}/${todos.length} complete \xB7 next: ${nextPending.content.slice(0, 80)}]`);
269251
269317
  }
269252
269318
  }
269253
- } catch {
269254
269319
  }
269255
269320
  if (ts.goal || ts.completedSteps.length > 0) {
269256
269321
  const done = ts.completedSteps.slice(-5).map((s2) => s2.slice(0, 60));
@@ -269731,6 +269796,8 @@ TASK: ${task}` : task;
269731
269796
  this._selfConsistencyVotes = 0;
269732
269797
  this._retrievalContextCache = null;
269733
269798
  this._loopBlockedTools = void 0;
269799
+ this._lastTodoWriteTurn = -1;
269800
+ this._lastTodoReminderTurn = -1;
269734
269801
  let pendingConstraintWarnings = [];
269735
269802
  let consecutiveTextOnly = 0;
269736
269803
  let loopInterventionCount = 0;
@@ -269813,6 +269880,17 @@ Integrate this guidance into your current approach. Continue working on the task
269813
269880
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
269814
269881
  });
269815
269882
  }
269883
+ {
269884
+ const maybeReminder = this.getTodoReminderContent(turn);
269885
+ if (maybeReminder) {
269886
+ messages2.push({ role: "user", content: maybeReminder });
269887
+ this.emit({
269888
+ type: "status",
269889
+ content: `todo_reminder injected (turn ${turn}, last todo_write turn ${this._lastTodoWriteTurn})`,
269890
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
269891
+ });
269892
+ }
269893
+ }
269816
269894
  const turnTier = this.options.modelTier ?? "large";
269817
269895
  if (turn === 0 && (turnTier === "small" || turnTier === "medium")) {
269818
269896
  const goal = this._taskState.goal || "";
@@ -270564,6 +270642,9 @@ ${memoryLines.join("\n")}`
270564
270642
  this._taskState.toolCallCount++;
270565
270643
  const filePath = typeof tc.arguments?.path === "string" ? tc.arguments.path : "";
270566
270644
  recordToolExecution(this._appState, tc.name, performance.now() - toolStart, result.success, filePath || void 0);
270645
+ if (tc.name === "todo_write") {
270646
+ this._lastTodoWriteTurn = turn;
270647
+ }
270567
270648
  if (tc.name === "file_read" || tc.name === "list_directory" || tc.name === "find_files" || tc.name === "grep_search") {
270568
270649
  this._taskState.currentStep = `exploring: ${filePath || String(tc.arguments?.pattern ?? tc.arguments?.path ?? "").slice(0, 60)}`;
270569
270650
  } else if (tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit") {
@@ -276281,6 +276362,7 @@ __export(dist_exports4, {
276281
276362
  cleanScaffolding: () => cleanScaffolding,
276282
276363
  clearTurnState: () => clearTurnState,
276283
276364
  compilePersonalityPrompt: () => compilePersonalityPrompt,
276365
+ computeTodoReminder: () => computeTodoReminder,
276284
276366
  createAppState: () => createAppState,
276285
276367
  createChildAbortController: () => createChildAbortController,
276286
276368
  deleteAgentTaskSidecar: () => deleteAgentTaskSidecar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.206",
3
+ "version": "0.187.207",
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",