open-agents-ai 0.187.11 → 0.187.13

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 +303 -33
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -259789,6 +259789,194 @@ var init_app_state = __esm({
259789
259789
  }
259790
259790
  });
259791
259791
 
259792
+ // packages/orchestrator/dist/streaming-executor.js
259793
+ var StreamingToolExecutor;
259794
+ var init_streaming_executor = __esm({
259795
+ "packages/orchestrator/dist/streaming-executor.js"() {
259796
+ "use strict";
259797
+ init_tool_batching();
259798
+ StreamingToolExecutor = class {
259799
+ tools = /* @__PURE__ */ new Map();
259800
+ insertionOrder = [];
259801
+ config;
259802
+ executeFn = null;
259803
+ constructor(config) {
259804
+ this.config = {
259805
+ maxConcurrent: config?.maxConcurrent ?? 5
259806
+ };
259807
+ }
259808
+ /** Set the tool execution function */
259809
+ setExecutor(fn) {
259810
+ this.executeFn = fn;
259811
+ }
259812
+ /** Number of tools tracked */
259813
+ get size() {
259814
+ return this.tools.size;
259815
+ }
259816
+ /** Whether any tools have been queued */
259817
+ get hasTools() {
259818
+ return this.tools.size > 0;
259819
+ }
259820
+ /**
259821
+ * Queue a new tool call from streaming response.
259822
+ * Called when a tool_use block is first detected in the stream.
259823
+ */
259824
+ queue(id, name10, partialArgs) {
259825
+ if (this.tools.has(id))
259826
+ return;
259827
+ this.tools.set(id, {
259828
+ id,
259829
+ name: name10,
259830
+ args: partialArgs ?? {},
259831
+ state: "queued",
259832
+ concurrencySafe: isConcurrencySafe(name10),
259833
+ finalized: false,
259834
+ queuedAt: Date.now()
259835
+ });
259836
+ this.insertionOrder.push(id);
259837
+ }
259838
+ /**
259839
+ * Finalize a tool call's arguments (stream complete for this tool_use block).
259840
+ * Parses the accumulated JSON args and triggers execution if conditions allow.
259841
+ */
259842
+ finalize(id, args) {
259843
+ const entry = this.tools.get(id);
259844
+ if (!entry)
259845
+ return;
259846
+ entry.args = args;
259847
+ entry.finalized = true;
259848
+ if (entry.state === "queued") {
259849
+ this.processQueue();
259850
+ }
259851
+ }
259852
+ /**
259853
+ * Get all completed tool results that haven't been yielded yet.
259854
+ * Respects ordering: stops at first non-yielded exclusive tool that's still running.
259855
+ * Marks returned entries as "yielded".
259856
+ */
259857
+ drainCompleted() {
259858
+ const results = [];
259859
+ for (const id of this.insertionOrder) {
259860
+ const entry = this.tools.get(id);
259861
+ if (!entry)
259862
+ continue;
259863
+ if (entry.state === "yielded")
259864
+ continue;
259865
+ if ((entry.state === "completed" || entry.state === "failed") && entry.result) {
259866
+ results.push({ id: entry.id, name: entry.name, result: entry.result });
259867
+ entry.state = "yielded";
259868
+ } else if (entry.state === "executing" && !entry.concurrencySafe) {
259869
+ break;
259870
+ }
259871
+ }
259872
+ return results;
259873
+ }
259874
+ /**
259875
+ * Wait for all queued/executing tools to complete.
259876
+ */
259877
+ async waitAll() {
259878
+ this.processQueue();
259879
+ while (true) {
259880
+ const pending = Array.from(this.tools.values()).filter((e2) => e2.state === "queued" || e2.state === "executing");
259881
+ if (pending.length === 0)
259882
+ break;
259883
+ const executing = pending.filter((e2) => e2.promise);
259884
+ if (executing.length > 0) {
259885
+ await Promise.allSettled(executing.map((e2) => e2.promise));
259886
+ this.processQueue();
259887
+ } else {
259888
+ this.processQueue();
259889
+ await new Promise((r2) => setTimeout(r2, 1));
259890
+ }
259891
+ }
259892
+ }
259893
+ /** Get the current state of all tool calls */
259894
+ getStates() {
259895
+ const states = /* @__PURE__ */ new Map();
259896
+ for (const [id, entry] of this.tools) {
259897
+ states.set(id, entry.state);
259898
+ }
259899
+ return states;
259900
+ }
259901
+ /** Get count of tools in each state */
259902
+ getCounts() {
259903
+ const counts = {
259904
+ queued: 0,
259905
+ executing: 0,
259906
+ completed: 0,
259907
+ failed: 0,
259908
+ yielded: 0
259909
+ };
259910
+ for (const entry of this.tools.values()) {
259911
+ counts[entry.state]++;
259912
+ }
259913
+ return counts;
259914
+ }
259915
+ /** Clear all tool entries (call between turns) */
259916
+ reset() {
259917
+ this.tools.clear();
259918
+ this.insertionOrder = [];
259919
+ }
259920
+ // ─────────────────────────────────────────────────────────
259921
+ // Internal — Hannover-aligned concurrency control
259922
+ // ─────────────────────────────────────────────────────────
259923
+ /**
259924
+ * Can this tool start executing now?
259925
+ * Rules (from Hannover StreamingToolExecutor.canExecuteTool):
259926
+ * 1. No tools executing → any tool can start
259927
+ * 2. New tool is concurrent-safe AND all executing are concurrent-safe → start
259928
+ * 3. Otherwise → wait
259929
+ */
259930
+ canExecute(entry) {
259931
+ if (!this.executeFn)
259932
+ return false;
259933
+ if (!entry.finalized)
259934
+ return false;
259935
+ const executing = Array.from(this.tools.values()).filter((e2) => e2.state === "executing");
259936
+ if (executing.length >= this.config.maxConcurrent)
259937
+ return false;
259938
+ if (executing.length === 0)
259939
+ return true;
259940
+ if (entry.concurrencySafe && executing.every((e2) => e2.concurrencySafe))
259941
+ return true;
259942
+ return false;
259943
+ }
259944
+ /**
259945
+ * Process the queue in insertion order.
259946
+ * Starts tools that can execute, stops at first exclusive tool that must wait.
259947
+ */
259948
+ processQueue() {
259949
+ for (const id of this.insertionOrder) {
259950
+ const entry = this.tools.get(id);
259951
+ if (!entry || entry.state !== "queued")
259952
+ continue;
259953
+ if (this.canExecute(entry)) {
259954
+ this.startExecution(entry);
259955
+ } else if (!entry.concurrencySafe) {
259956
+ break;
259957
+ }
259958
+ }
259959
+ }
259960
+ startExecution(entry) {
259961
+ entry.state = "executing";
259962
+ entry.startedAt = Date.now();
259963
+ const exec4 = this.executeFn;
259964
+ entry.promise = exec4(entry.name, entry.args).then((result) => {
259965
+ entry.state = "completed";
259966
+ entry.result = result;
259967
+ entry.completedAt = Date.now();
259968
+ this.processQueue();
259969
+ }).catch((err) => {
259970
+ entry.state = "failed";
259971
+ entry.result = { success: false, output: "", error: String(err) };
259972
+ entry.completedAt = Date.now();
259973
+ this.processQueue();
259974
+ });
259975
+ }
259976
+ };
259977
+ }
259978
+ });
259979
+
259792
259980
  // packages/orchestrator/dist/agenticRunner.js
259793
259981
  function repairJson(raw) {
259794
259982
  if (!raw || typeof raw !== "string")
@@ -259865,6 +260053,7 @@ var init_agenticRunner = __esm({
259865
260053
  init_tool_batching();
259866
260054
  init_hooks();
259867
260055
  init_app_state();
260056
+ init_streaming_executor();
259868
260057
  SYSTEM_PROMPT = loadPrompt("agentic/system-large.md");
259869
260058
  SYSTEM_PROMPT_MEDIUM = loadPrompt("agentic/system-medium.md");
259870
260059
  SYSTEM_PROMPT_SMALL = loadPrompt("agentic/system-small.md");
@@ -259884,6 +260073,7 @@ var init_agenticRunner = __esm({
259884
260073
  _skillCompactionStrategy = null;
259885
260074
  _hookManager = new HookManager();
259886
260075
  _appState = createAppState();
260076
+ _streamingExecutor = new StreamingToolExecutor({ maxConcurrent: 5 });
259887
260077
  // -- Task State Tracking (Priority 3) --
259888
260078
  _taskState = {
259889
260079
  goal: "",
@@ -261016,25 +261206,30 @@ Then use file_read on individual FILES inside it.`);
261016
261206
  return { tc, output };
261017
261207
  };
261018
261208
  const rawToolCalls = msg.toolCalls;
261019
- const batchToolCalls = rawToolCalls.map((tc) => ({
261020
- name: tc.name,
261021
- args: tc.arguments,
261022
- id: tc.id
261023
- }));
261024
- const batches = partitionToolCalls(batchToolCalls);
261025
- for (const batch2 of batches) {
261026
- if (this.aborted)
261027
- break;
261028
- const results = await executeBatch(batch2, async (call) => {
261029
- const originalTc = rawToolCalls.find((tc) => tc.id === call.id);
261030
- return executeSingle(originalTc);
261031
- }, 5);
261032
- for (const r2 of results) {
261033
- if (r2) {
261034
- messages2.push(this.buildToolMessage(r2.output, r2.tc.id));
261035
- if (r2.tc.name === "task_complete") {
261209
+ if (this.options.streamEnabled && this._streamingExecutor.hasTools) {
261210
+ this._streamingExecutor.setExecutor(async (name10, args) => {
261211
+ const matchTc = rawToolCalls.find((tc) => tc.name === name10 && JSON.stringify(tc.arguments) === JSON.stringify(args)) ?? rawToolCalls.find((tc) => tc.name === name10);
261212
+ if (!matchTc)
261213
+ return { success: false, output: "", error: `Tool ${name10} not found in response` };
261214
+ const r2 = await executeSingle(matchTc);
261215
+ if (!r2)
261216
+ return { success: false, output: "", error: "aborted" };
261217
+ const isError2 = r2.output.startsWith("Error:");
261218
+ return { success: !isError2, output: r2.output, error: isError2 ? r2.output : void 0 };
261219
+ });
261220
+ await this._streamingExecutor.waitAll();
261221
+ const streamResults = this._streamingExecutor.drainCompleted();
261222
+ const handledIds = /* @__PURE__ */ new Set();
261223
+ for (const sr of streamResults) {
261224
+ const matchTc = rawToolCalls.find((tc) => tc.id === sr.id) ?? rawToolCalls.find((tc) => tc.name === sr.name && !handledIds.has(tc.id));
261225
+ if (matchTc) {
261226
+ handledIds.add(matchTc.id);
261227
+ const output = sr.result.success ? sr.result.output : `Error: ${sr.result.error || "unknown"}
261228
+ ${sr.result.output}`;
261229
+ messages2.push(this.buildToolMessage(output, matchTc.id));
261230
+ if (matchTc.name === "task_complete") {
261036
261231
  completed = true;
261037
- summary = r2.tc.arguments.summary || "";
261232
+ summary = matchTc.arguments.summary || "";
261038
261233
  if (summary && !this._assistantTextEmitted) {
261039
261234
  this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
261040
261235
  this._assistantTextEmitted = true;
@@ -261043,8 +261238,57 @@ Then use file_read on individual FILES inside it.`);
261043
261238
  }
261044
261239
  }
261045
261240
  }
261046
- if (completed)
261047
- break;
261241
+ if (!completed) {
261242
+ const unhandled = rawToolCalls.filter((tc) => !handledIds.has(tc.id));
261243
+ for (const tc of unhandled) {
261244
+ if (this.aborted)
261245
+ break;
261246
+ const r2 = await executeSingle(tc);
261247
+ if (r2) {
261248
+ messages2.push(this.buildToolMessage(r2.output, r2.tc.id));
261249
+ if (r2.tc.name === "task_complete") {
261250
+ completed = true;
261251
+ summary = r2.tc.arguments.summary || "";
261252
+ if (summary && !this._assistantTextEmitted) {
261253
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
261254
+ this._assistantTextEmitted = true;
261255
+ }
261256
+ break;
261257
+ }
261258
+ }
261259
+ }
261260
+ }
261261
+ } else {
261262
+ const batchToolCalls = rawToolCalls.map((tc) => ({
261263
+ name: tc.name,
261264
+ args: tc.arguments,
261265
+ id: tc.id
261266
+ }));
261267
+ const batches = partitionToolCalls(batchToolCalls);
261268
+ for (const batch2 of batches) {
261269
+ if (this.aborted)
261270
+ break;
261271
+ const results = await executeBatch(batch2, async (call) => {
261272
+ const originalTc = rawToolCalls.find((tc) => tc.id === call.id);
261273
+ return executeSingle(originalTc);
261274
+ }, 5);
261275
+ for (const r2 of results) {
261276
+ if (r2) {
261277
+ messages2.push(this.buildToolMessage(r2.output, r2.tc.id));
261278
+ if (r2.tc.name === "task_complete") {
261279
+ completed = true;
261280
+ summary = r2.tc.arguments.summary || "";
261281
+ if (summary && !this._assistantTextEmitted) {
261282
+ this.emit({ type: "assistant_text", content: summary, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
261283
+ this._assistantTextEmitted = true;
261284
+ }
261285
+ break;
261286
+ }
261287
+ }
261288
+ }
261289
+ if (completed)
261290
+ break;
261291
+ }
261048
261292
  }
261049
261293
  if (completed)
261050
261294
  break;
@@ -262963,6 +263207,8 @@ ${description}`
262963
263207
  const toolCallAccumulators = /* @__PURE__ */ new Map();
262964
263208
  let streamUsage;
262965
263209
  this.emit({ type: "stream_start", turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
263210
+ this._streamingExecutor.reset();
263211
+ let lastFinalizedIdx = -1;
262966
263212
  for await (const chunk of backend.chatCompletionStream(request)) {
262967
263213
  if (this.aborted)
262968
263214
  break;
@@ -263014,21 +263260,52 @@ ${description}`
263014
263260
  }
263015
263261
  if (chunk.type === "tool_call_delta") {
263016
263262
  const idx = chunk.toolCallIndex ?? 0;
263263
+ if (idx > lastFinalizedIdx && lastFinalizedIdx >= 0) {
263264
+ const prevAcc = toolCallAccumulators.get(lastFinalizedIdx);
263265
+ if (prevAcc && prevAcc.name) {
263266
+ let parsedArgs;
263267
+ try {
263268
+ parsedArgs = JSON.parse(prevAcc.args);
263269
+ } catch {
263270
+ parsedArgs = repairJson(prevAcc.args) ?? { _raw: prevAcc.args };
263271
+ }
263272
+ this._streamingExecutor.finalize(prevAcc.id, parsedArgs);
263273
+ }
263274
+ }
263017
263275
  if (!toolCallAccumulators.has(idx)) {
263018
- toolCallAccumulators.set(idx, {
263019
- id: chunk.toolCallId ?? crypto.randomUUID(),
263020
- name: chunk.toolCallName ?? "",
263021
- args: ""
263022
- });
263276
+ const newId = chunk.toolCallId ?? crypto.randomUUID();
263277
+ const newName = chunk.toolCallName ?? "";
263278
+ toolCallAccumulators.set(idx, { id: newId, name: newName, args: "" });
263279
+ if (newName) {
263280
+ this._streamingExecutor.queue(newId, newName);
263281
+ }
263023
263282
  }
263024
263283
  const acc = toolCallAccumulators.get(idx);
263025
- if (chunk.toolCallName)
263284
+ if (chunk.toolCallName) {
263026
263285
  acc.name = chunk.toolCallName;
263286
+ if (!this._streamingExecutor.getStates().has(acc.id) && acc.name) {
263287
+ this._streamingExecutor.queue(acc.id, acc.name);
263288
+ }
263289
+ }
263027
263290
  if (chunk.toolCallId)
263028
263291
  acc.id = chunk.toolCallId;
263029
263292
  if (chunk.toolCallArgs) {
263030
263293
  acc.args += chunk.toolCallArgs;
263031
263294
  }
263295
+ lastFinalizedIdx = idx;
263296
+ }
263297
+ }
263298
+ if (toolCallAccumulators.size > 0) {
263299
+ for (const [, acc] of toolCallAccumulators) {
263300
+ if (acc.name && !["completed", "failed", "yielded", "executing"].includes(this._streamingExecutor.getStates().get(acc.id) ?? "")) {
263301
+ let parsedArgs;
263302
+ try {
263303
+ parsedArgs = JSON.parse(acc.args || "{}");
263304
+ } catch {
263305
+ parsedArgs = repairJson(acc.args) ?? { _raw: acc.args };
263306
+ }
263307
+ this._streamingExecutor.finalize(acc.id, parsedArgs);
263308
+ }
263032
263309
  }
263033
263310
  }
263034
263311
  this.emit({ type: "stream_end", content, turn, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
@@ -265126,13 +265403,6 @@ var init_skill_fork = __esm({
265126
265403
  }
265127
265404
  });
265128
265405
 
265129
- // packages/orchestrator/dist/streaming-executor.js
265130
- var init_streaming_executor = __esm({
265131
- "packages/orchestrator/dist/streaming-executor.js"() {
265132
- "use strict";
265133
- }
265134
- });
265135
-
265136
265406
  // packages/orchestrator/dist/index.js
265137
265407
  var init_dist7 = __esm({
265138
265408
  "packages/orchestrator/dist/index.js"() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.11",
3
+ "version": "0.187.13",
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",