open-agents-ai 0.88.0 → 0.90.0

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 +155 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -16487,6 +16487,8 @@ TASK: ${task}` : task }
16487
16487
  let completed = false;
16488
16488
  let summary = "";
16489
16489
  let bruteForceCycle = 0;
16490
+ let consecutiveTextOnly = 0;
16491
+ const MAX_CONSECUTIVE_TEXT_ONLY = 3;
16490
16492
  for (let turn = 0; turn < this.options.maxTurns; turn++) {
16491
16493
  if (this._paused) {
16492
16494
  const shouldContinue = await this.waitIfPaused();
@@ -16600,6 +16602,7 @@ Integrate this guidance into your current approach. Continue working on the task
16600
16602
  break;
16601
16603
  const msg = choice.message;
16602
16604
  if (msg.toolCalls && msg.toolCalls.length > 0) {
16605
+ consecutiveTextOnly = 0;
16603
16606
  messages.push({
16604
16607
  role: "assistant",
16605
16608
  content: msg.content || null,
@@ -16775,6 +16778,7 @@ Do NOT continue walking up parent directories.`);
16775
16778
  } else {
16776
16779
  const content = msg.content || "";
16777
16780
  messages.push({ role: "assistant", content });
16781
+ consecutiveTextOnly++;
16778
16782
  this.emit({
16779
16783
  type: "model_response",
16780
16784
  content: content.slice(0, 200),
@@ -16786,15 +16790,34 @@ Do NOT continue walking up parent directories.`);
16786
16790
  summary = content;
16787
16791
  break;
16788
16792
  }
16793
+ if (consecutiveTextOnly >= MAX_CONSECUTIVE_TEXT_ONLY) {
16794
+ this.emit({
16795
+ type: "status",
16796
+ content: `Model returned ${consecutiveTextOnly} consecutive text-only responses without using tools \u2014 breaking turn loop to avoid runaway`,
16797
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
16798
+ });
16799
+ break;
16800
+ }
16789
16801
  messages.push({
16790
16802
  role: "user",
16791
16803
  content: "Continue working. Use tools to read files, make changes, and run validation. Call task_complete when done."
16792
16804
  });
16793
16805
  }
16794
16806
  }
16807
+ let prevCycleToolCalls = toolCallCount;
16795
16808
  while (!completed && !this.aborted && this.options.bruteForce && bruteForceCycle < this.options.bruteForceMaxCycles) {
16796
16809
  bruteForceCycle++;
16797
16810
  const totalTurns = messages.filter((m) => m.role === "assistant").length;
16811
+ if (bruteForceCycle > 1 && toolCallCount === prevCycleToolCalls) {
16812
+ this.emit({
16813
+ type: "status",
16814
+ content: `Stopping re-engagement \u2014 no tool call progress in cycle ${bruteForceCycle - 1}. Model may not support tool calling for this task.`,
16815
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
16816
+ });
16817
+ break;
16818
+ }
16819
+ prevCycleToolCalls = toolCallCount;
16820
+ consecutiveTextOnly = 0;
16798
16821
  this.emit({
16799
16822
  type: "status",
16800
16823
  content: `Re-engaging \u2014 cycle ${bruteForceCycle} (${totalTurns} turns, ${toolCallCount} tool calls so far)`,
@@ -16918,6 +16941,7 @@ Integrate this guidance into your current approach. Continue working on the task
16918
16941
  break;
16919
16942
  const msg = choice.message;
16920
16943
  if (msg.toolCalls && msg.toolCalls.length > 0) {
16944
+ consecutiveTextOnly = 0;
16921
16945
  messages.push({ role: "assistant", content: msg.content || null, tool_calls: msg.toolCalls.map((tc) => ({ id: tc.id, type: "function", function: { name: tc.name, arguments: JSON.stringify(tc.arguments) } })) });
16922
16946
  for (const tc of msg.toolCalls) {
16923
16947
  if (this.aborted)
@@ -16978,12 +17002,21 @@ Do NOT continue walking up parent directories.`);
16978
17002
  } else {
16979
17003
  const content = msg.content || "";
16980
17004
  messages.push({ role: "assistant", content });
17005
+ consecutiveTextOnly++;
16981
17006
  this.emit({ type: "model_response", content: content.slice(0, 200), turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
16982
17007
  if (/task.?complete|all tests pass/i.test(content)) {
16983
17008
  completed = true;
16984
17009
  summary = content;
16985
17010
  break;
16986
17011
  }
17012
+ if (consecutiveTextOnly >= MAX_CONSECUTIVE_TEXT_ONLY) {
17013
+ this.emit({
17014
+ type: "status",
17015
+ content: `Model returned ${consecutiveTextOnly} consecutive text-only responses \u2014 breaking cycle ${bruteForceCycle}`,
17016
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
17017
+ });
17018
+ break;
17019
+ }
16987
17020
  messages.push({ role: "user", content: "Continue working. Use tools to read files, make changes, and run validation. Call task_complete when done." });
16988
17021
  }
16989
17022
  }
@@ -18325,6 +18358,49 @@ var init_nexusBackend = __esm({
18325
18358
  usage: this.extractUsage(responseData)
18326
18359
  };
18327
18360
  }
18361
+ /**
18362
+ * Simulated streaming: calls chatCompletion() for the full response,
18363
+ * then yields it as word-level StreamChunk objects.
18364
+ * Gives API compatibility with the streaming path in AgenticRunner.
18365
+ */
18366
+ async *chatCompletionStream(request) {
18367
+ const response = await this.chatCompletion(request);
18368
+ const choice = response.choices[0];
18369
+ if (!choice)
18370
+ return;
18371
+ const msg = choice.message;
18372
+ if (msg.content) {
18373
+ const words = msg.content.split(/(\s+)/);
18374
+ for (const word of words) {
18375
+ if (word) {
18376
+ yield { type: "content", content: word };
18377
+ }
18378
+ }
18379
+ }
18380
+ if (msg.toolCalls) {
18381
+ for (let i = 0; i < msg.toolCalls.length; i++) {
18382
+ const tc = msg.toolCalls[i];
18383
+ yield {
18384
+ type: "tool_call_delta",
18385
+ toolCallIndex: i,
18386
+ toolCallId: tc.id,
18387
+ toolCallName: tc.name,
18388
+ toolCallArgs: JSON.stringify(tc.arguments)
18389
+ };
18390
+ }
18391
+ }
18392
+ if (response.usage) {
18393
+ yield {
18394
+ type: "usage",
18395
+ usage: {
18396
+ promptTokens: response.usage.promptTokens ?? 0,
18397
+ completionTokens: response.usage.completionTokens ?? 0,
18398
+ totalTokens: response.usage.totalTokens
18399
+ }
18400
+ };
18401
+ }
18402
+ yield { type: "finish", finishReason: "stop" };
18403
+ }
18328
18404
  extractUsage(data) {
18329
18405
  const usage = data.usage;
18330
18406
  if (!usage)
@@ -18341,6 +18417,60 @@ var init_nexusBackend = __esm({
18341
18417
  }
18342
18418
  });
18343
18419
 
18420
+ // packages/orchestrator/dist/flowstatePrompt.js
18421
+ var FLOWSTATE_PROMPT;
18422
+ var init_flowstatePrompt = __esm({
18423
+ "packages/orchestrator/dist/flowstatePrompt.js"() {
18424
+ "use strict";
18425
+ FLOWSTATE_PROMPT = `## FlowState Protocol (Active)
18426
+
18427
+ You are operating in FlowState mode. Apply this protocol to every task:
18428
+
18429
+ ### Pre-Task (Engage)
18430
+ - Clear context of noise \u2014 compact if SNR is degraded
18431
+ - Set ridiculously specific sub-goals (not "fix things" but "change line 47 of auth.ts to check for null")
18432
+ - Begin immediately \u2014 first tool call in first response (response inhibition)
18433
+ - Identify the first concrete action before reasoning further
18434
+
18435
+ ### During Task (Struggle -> Release -> Flow)
18436
+ - Execute the highest-impact action first (goal-directedness)
18437
+ - Maintain tight feedback loops: execute, verify, learn (immediate feedback)
18438
+ - Calibrate task complexity: chunk if overwhelming, compress if trivial (challenge-skill balance)
18439
+ - Rotate approaches when stuck: different tools, different angles (oasis effect)
18440
+ - Minimize context pollution: archive large outputs, compact when needed (minimalism)
18441
+ - Never research when you can act; never read when you can execute
18442
+ - Track your own token allocation: maximize flow-tokens (core reasoning), minimize junk-tokens (unnecessary elaboration)
18443
+
18444
+ ### Between Tasks (Recovery)
18445
+ - Compact context aggressively (active recovery, not passive degradation)
18446
+ - Update persistent memory with learnings (consolidation)
18447
+ - Clear the todo list \u2014 identify next highest-impact action (power-down ritual)
18448
+ - Assess context quality and improve if degraded
18449
+
18450
+ ### Flowstate Checklist (verify before each major action)
18451
+ - [ ] Is the goal ridiculously clear? (specific file, line, change \u2014 not vague intent)
18452
+ - [ ] Is context clean? (no irrelevant history cluttering working memory)
18453
+ - [ ] Is the first action identified? (a specific tool call, not more research)
18454
+ - [ ] Is the feedback loop tight? (verification command defined before making changes)
18455
+ - [ ] Is recovery planned? (compaction strategy selected for after task)
18456
+
18457
+ ### The Diagnostic Compass (when stuck)
18458
+ | Symptom | Variable | Fix |
18459
+ |---|---|---|
18460
+ | Working on the wrong thing | Goal-Directedness | Re-target. Find the most direct path. |
18461
+ | Underleveraged | Leverage | Parallelize. Use sub-agents. Build reusable systems. |
18462
+ | Distracted, fractured, unable to sustain depth | Flow | Remove friction. Reduce cognitive load. Create immersion. |
18463
+
18464
+ ### Peak Performance Equation
18465
+ Peak Performance = Goal-Directedness x Leverage x Flow
18466
+ (These MULTIPLY \u2014 a zero in any one collapses the entire equation)
18467
+
18468
+ ### Mantra
18469
+ Flow follows focus. Focus follows clarity. Clarity follows constraint.
18470
+ Constrain your context. Clarify your target. Focus your tools. Flow will follow.`;
18471
+ }
18472
+ });
18473
+
18344
18474
  // packages/orchestrator/dist/costTracker.js
18345
18475
  var DEFAULT_PRICING, CostTracker;
18346
18476
  var init_costTracker = __esm({
@@ -18821,6 +18951,7 @@ var init_dist5 = __esm({
18821
18951
  init_retryController();
18822
18952
  init_agenticRunner();
18823
18953
  init_nexusBackend();
18954
+ init_flowstatePrompt();
18824
18955
  init_costTracker();
18825
18956
  init_workEvaluator();
18826
18957
  init_sessionMetrics();
@@ -23674,6 +23805,7 @@ function renderSlashHelp() {
23674
23805
  ["/compact <strategy>", "Compact with strategy: aggressive, decisions, errors, summary, structured"],
23675
23806
  ["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
23676
23807
  ["/deep", "Toggle deep context \u2014 relaxes compaction so large models use 85% of context"],
23808
+ ["/flow", "Toggle FlowState protocol \u2014 immediate action, tight loops, goal clarity"],
23677
23809
  ["/tools", "List agent-created custom tools"],
23678
23810
  ["/skills", "List available AIWG skills"],
23679
23811
  ["/skills <keyword>", "Filter skills by name or trigger"],
@@ -29386,6 +29518,18 @@ async function handleSlashCommand(input, ctx) {
29386
29518
  renderInfo(`Deep context mode: ${isOn ? "ON" : "OFF"}${hasLocal ? " (project-local)" : ""}` + (isOn ? " \u2014 compaction relaxed to 85% of context window. Large models will retain more working memory for complex reasoning." : " \u2014 compaction restored to default thresholds."));
29387
29519
  return "handled";
29388
29520
  }
29521
+ case "flow":
29522
+ case "flowstate": {
29523
+ if (!ctx.flowToggle) {
29524
+ renderWarning("FlowState not available");
29525
+ return "handled";
29526
+ }
29527
+ const isFlowOn = ctx.flowToggle();
29528
+ const flowSave = hasLocal ? ctx.saveLocalSettings.bind(ctx) : ctx.saveSettings.bind(ctx);
29529
+ flowSave({ flow: isFlowOn });
29530
+ renderInfo(`FlowState protocol: ${isFlowOn ? "ON" : "OFF"}${hasLocal ? " (project-local)" : ""}` + (isFlowOn ? " \u2014 agent will follow FlowState protocol: immediate action, tight feedback loops, goal clarity, challenge-skill calibration." : " \u2014 standard agent behavior restored."));
29531
+ return "handled";
29532
+ }
29389
29533
  case "emojis":
29390
29534
  case "emoji": {
29391
29535
  const current = ctx.getEmojis?.() ?? true;
@@ -39630,7 +39774,7 @@ function formatDMNToolArgs(toolName, args) {
39630
39774
  return "";
39631
39775
  }
39632
39776
  }
39633
- function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine) {
39777
+ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce, statusBar, sudoCallback, costTracker, onComplete, taskType, contextWindowSize, modelCaps, personality, deepContext, onCompaction, emotionEngine, flowEnabled) {
39634
39778
  const voiceStyleMap = {
39635
39779
  concise: 1,
39636
39780
  balanced: 3,
@@ -39644,6 +39788,9 @@ function startTask(task, config, repoRoot, voice, stream, taskStores, bruteForce
39644
39788
  if (taskType && modelTier !== "small") {
39645
39789
  dynamicContext += "\n\n" + buildTaskContext(taskType);
39646
39790
  }
39791
+ if (flowEnabled) {
39792
+ dynamicContext += "\n\n" + FLOWSTATE_PROMPT;
39793
+ }
39647
39794
  let backend;
39648
39795
  if (config.backendType === "nexus") {
39649
39796
  const nexusTool = new NexusTool(repoRoot);
@@ -40042,6 +40189,7 @@ async function startInteractive(config, repoPath) {
40042
40189
  let bruteForceEnabled = savedSettings.bruteforce ?? true;
40043
40190
  let currentStyle = PRESET_NAMES.includes(savedSettings.style) ? savedSettings.style : "balanced";
40044
40191
  let deepContextEnabled = savedSettings.deepContext ?? false;
40192
+ let flowEnabled = savedSettings.flow === true;
40045
40193
  if (savedSettings.emojis !== void 0)
40046
40194
  setEmojisEnabled(savedSettings.emojis);
40047
40195
  if (savedSettings.colors !== void 0)
@@ -40574,6 +40722,10 @@ Rationale: ${proposal.rationale}${provenanceNote}`;
40574
40722
  deepContextEnabled = !deepContextEnabled;
40575
40723
  return deepContextEnabled;
40576
40724
  },
40725
+ flowToggle() {
40726
+ flowEnabled = !flowEnabled;
40727
+ return flowEnabled;
40728
+ },
40577
40729
  setStyle(preset) {
40578
40730
  currentStyle = preset;
40579
40731
  },
@@ -41547,7 +41699,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
41547
41699
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary, meta) => {
41548
41700
  lastCompletedSummary = summary;
41549
41701
  lastTaskMeta = meta ?? null;
41550
- }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine);
41702
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled);
41551
41703
  activeTask = task;
41552
41704
  showPrompt();
41553
41705
  await task.promise;
@@ -41764,7 +41916,7 @@ NEW TASK: ${fullInput}`;
41764
41916
  }, bruteForceEnabled, statusBar, handleSudoRequest, costTracker, (summary, meta) => {
41765
41917
  lastCompletedSummary = summary;
41766
41918
  lastTaskMeta = meta ?? null;
41767
- }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine);
41919
+ }, currentTaskType, resolvedContextWindowSize, resolvedCaps, currentStyle, deepContextEnabled, compactionSNRCallback(), emotionEngine, flowEnabled);
41768
41920
  activeTask = task;
41769
41921
  showPrompt();
41770
41922
  await task.promise;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.88.0",
3
+ "version": "0.90.0",
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",