open-agents-ai 0.187.449 → 0.187.450

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.
package/dist/index.js CHANGED
@@ -505795,12 +505795,16 @@ var init_agent_tool = __esm({
505795
505795
  subAgentMode: true
505796
505796
  });
505797
505797
  if (result.completed) {
505798
+ const filesLine = result.filesModified && result.filesModified.length > 0 ? `
505799
+ Files modified: ${result.filesModified.slice(0, 8).join(", ")}${result.filesModified.length > 8 ? ` (+${result.filesModified.length - 8} more)` : ""}` : "";
505800
+ const artifactLine = result.artifactIds && result.artifactIds.length > 0 ? `
505801
+ Artifacts: ${result.artifactIds.join(", ")} (call memex_retrieve to view full sub-agent transcript)` : "";
505798
505802
  return {
505799
505803
  success: true,
505800
505804
  output: `Agent completed: ${result.summary}
505801
505805
  Type: ${subagentType}
505802
505806
  Turns: ${result.turns}, Tool calls: ${result.toolCalls}
505803
- Duration: ${(result.durationMs / 1e3).toFixed(1)}s`,
505807
+ Duration: ${(result.durationMs / 1e3).toFixed(1)}s` + filesLine + artifactLine,
505804
505808
  durationMs: performance.now() - start2
505805
505809
  };
505806
505810
  }
@@ -516588,6 +516592,13 @@ var init_contextTree = __esm({
516588
516592
  * Contract the named phase: extract anchors, generate a stub summary,
516589
516593
  * mark the node `contracted`. The caller may then archive via the
516590
516594
  * archive callback (Phase 1 hands off to memex).
516595
+ *
516596
+ * `summarizer` may be sync (returns string) or async (returns Promise<string>).
516597
+ * When async, the node is contracted IMMEDIATELY with the default stub
516598
+ * summary; the real summary lands later via the returned promise's
516599
+ * resolution updating `node.summary` in place. The next compactMessages
516600
+ * render therefore picks up the LLM summary without blocking the turn
516601
+ * loop on it.
516591
516602
  */
516592
516603
  contract(phase, turn, summarizer) {
516593
516604
  const node = this.snapshot.phases[phase];
@@ -516595,9 +516606,24 @@ var init_contextTree = __esm({
516595
516606
  return null;
516596
516607
  const anchors = extractAnchorsFromMessages(node.messages, turn).slice(0, this.opts.maxAnchorsPerNode);
516597
516608
  node.anchors = [...node.anchors, ...anchors].slice(-this.opts.maxAnchorsPerNode);
516598
- node.summary = summarizer ? summarizer(node.messages) : this.defaultSummary(phase, node.messages);
516609
+ node.summary = this.defaultSummary(phase, node.messages);
516599
516610
  node.status = "contracted";
516600
516611
  node.contractedAtTurn = turn;
516612
+ if (summarizer) {
516613
+ try {
516614
+ const result = summarizer(node.messages);
516615
+ if (result && typeof result.then === "function") {
516616
+ result.then((s2) => {
516617
+ if (typeof s2 === "string" && s2.length > 0)
516618
+ node.summary = s2;
516619
+ }).catch(() => {
516620
+ });
516621
+ } else if (typeof result === "string") {
516622
+ node.summary = result;
516623
+ }
516624
+ } catch {
516625
+ }
516626
+ }
516601
516627
  this.snapshot.history.push({ ...node, phase });
516602
516628
  if (this.snapshot.history.length > 32) {
516603
516629
  this.snapshot.history = this.snapshot.history.slice(-32);
@@ -517726,6 +517752,11 @@ var init_agenticRunner = __esm({
517726
517752
  // by keyword. Initialized lazily in run() because the system-prompt hash
517727
517753
  // depends on the actual prompt resolved at run time.
517728
517754
  _contextTree = null;
517755
+ // Phase 1 — index in `messages` where the current phase's slice begins.
517756
+ // On phase transition we capture messages[_phaseMessageStartIdx..now]
517757
+ // as the OUTGOING phase's owned slice (via tree.observePhaseMessages),
517758
+ // then advance the cursor so the new phase starts fresh.
517759
+ _phaseMessageStartIdx = 0;
517729
517760
  // Phase 6 — last-surface guard so we don't re-inject the same anchor on
517730
517761
  // consecutive turns when its keywords still match.
517731
517762
  _lastSurfacedAnchorIds = /* @__PURE__ */ new Set();
@@ -517816,6 +517847,32 @@ var init_agenticRunner = __esm({
517816
517847
  return this._fileRegistry;
517817
517848
  }
517818
517849
  /** Get a Memex archive entry by ID (for the dereference tool) */
517850
+ /**
517851
+ * Phase 4 (Task C) — public read access to the runner's TaskState. Used
517852
+ * by the CLI's spawnInProcess wiring to extract `modifiedFiles` from a
517853
+ * sub-agent run and surface them on the parent's `InProcessResult`. The
517854
+ * caller treats the returned object as read-only.
517855
+ */
517856
+ getTaskState() {
517857
+ return this._taskState;
517858
+ }
517859
+ /**
517860
+ * Phase 4 (Task C) — direct memex archive helper for parent runners
517861
+ * that want to file a sub-agent's verbose result under a known id.
517862
+ * Returns the assigned id so the caller can surface it on the result.
517863
+ */
517864
+ archiveToMemex(opts) {
517865
+ const id = opts.id ?? this.quickHash(`${opts.toolName}|${opts.summary}|${Date.now()}`);
517866
+ this._memexArchive.set(id, {
517867
+ id,
517868
+ toolName: opts.toolName,
517869
+ summary: opts.summary,
517870
+ fullContent: opts.fullContent,
517871
+ turn: opts.turn ?? this._taskState.toolCallCount,
517872
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
517873
+ });
517874
+ return id;
517875
+ }
517819
517876
  getMemexEntry(id) {
517820
517877
  return this._memexArchive.get(id);
517821
517878
  }
@@ -518326,6 +518383,59 @@ ${body}`;
518326
518383
  });
518327
518384
  }
518328
518385
  }
518386
+ /**
518387
+ * Phase 1 (Task B) — build an async LLM summarizer for ContextTree.contract().
518388
+ *
518389
+ * Returns a function that takes a phase's owned message slice and asks the
518390
+ * backend for a 2-3 sentence summary of what the phase accomplished. Hard
518391
+ * caps the messages slice at ~3000 chars (≈750 tokens) of representative
518392
+ * content to keep the cost bounded. Never blocks the turn loop — the
518393
+ * returned promise updates ContextNode.summary in place when it resolves.
518394
+ *
518395
+ * Returns null when the disable knob is set or the backend is missing the
518396
+ * chatCompletion method.
518397
+ */
518398
+ makePhaseSummarizer() {
518399
+ if (process.env["OA_DISABLE_PHASE_SUMMARIZER"] === "1")
518400
+ return null;
518401
+ if (!this.backend || typeof this.backend.chatCompletion !== "function")
518402
+ return null;
518403
+ return async (phase, msgs) => {
518404
+ const tail = msgs.slice(-8);
518405
+ const blob = tail.map((m2) => {
518406
+ const role = m2.role;
518407
+ const content = typeof m2.content === "string" ? m2.content.slice(0, 400) : "";
518408
+ if (m2.role === "assistant" && m2.tool_calls && m2.tool_calls.length > 0) {
518409
+ const calls = m2.tool_calls.map((tc) => `${tc.function.name}(${(tc.function.arguments || "").slice(0, 80)})`).join(", ");
518410
+ return `[${role}] ${content || ""} | tool_calls: ${calls}`.slice(0, 500);
518411
+ }
518412
+ return `[${role}] ${content}`.slice(0, 500);
518413
+ }).join("\n");
518414
+ const prompt = `You are summarizing the "${phase}" phase of an agent's task. Below is the message slice owned by this phase. Write a 2-3 sentence summary focused on WHAT was done and any KEY FINDINGS. No fluff. No "the agent did X" framing — just the facts.
518415
+
518416
+ === ${phase} phase messages ===
518417
+ ${blob}
518418
+ === end ===`;
518419
+ try {
518420
+ const response = await this.backend.chatCompletion({
518421
+ messages: [
518422
+ { role: "system", content: "You write extremely concise phase summaries for agent context management." },
518423
+ { role: "user", content: prompt }
518424
+ ],
518425
+ tools: [],
518426
+ temperature: 0,
518427
+ maxTokens: 256,
518428
+ timeoutMs: 3e4
518429
+ });
518430
+ const text = response.choices?.[0]?.message?.content;
518431
+ if (typeof text === "string" && text.length > 0) {
518432
+ return `${phase}: ${text.replace(/\s+/g, " ").trim().slice(0, 600)}`;
518433
+ }
518434
+ } catch {
518435
+ }
518436
+ return "";
518437
+ };
518438
+ }
518329
518439
  /**
518330
518440
  * Phase 6 — Anchor Surfacing.
518331
518441
  *
@@ -518857,6 +518967,7 @@ Respond with your assessment, then take action.`;
518857
518967
  const contextComposition = await this.assembleContext(task, context2);
518858
518968
  const systemPrompt = contextComposition.assembled;
518859
518969
  this._contextTree = new ContextTree(`sys-${systemPrompt.length}`, cleanedTask.slice(0, 200));
518970
+ this._phaseMessageStartIdx = 2;
518860
518971
  this.emit({
518861
518972
  type: "status",
518862
518973
  content: `Context assembled: ${contextComposition.sections.map((s2) => `${s2.label}(${s2.tokenEstimate}t)`).join(" + ")} = ~${contextComposition.totalTokenEstimate}t`,
@@ -519610,19 +519721,28 @@ ${memoryLines.join("\n")}`
519610
519721
  this._contextTree.observeToolCall(tc.name, argsKey, turn);
519611
519722
  const transition = this._contextTree.maybeTransition(turn);
519612
519723
  if (transition) {
519724
+ const phaseSlice = messages2.slice(this._phaseMessageStartIdx);
519725
+ if (phaseSlice.length > 0) {
519726
+ const fromNode = this._contextTree.getSnapshot().phases[transition.from];
519727
+ if (fromNode) {
519728
+ fromNode.messages = phaseSlice;
519729
+ }
519730
+ }
519613
519731
  this.emit({
519614
519732
  type: "status",
519615
519733
  content: `Phase: ${transition.from} → ${transition.to}`,
519616
519734
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
519617
519735
  });
519618
- const contracted = this._contextTree.contractInactive(turn);
519736
+ const summarizer = this.makePhaseSummarizer();
519737
+ const contracted = this._contextTree.contractInactive(turn, summarizer ? ((msgs) => summarizer(transition.from, msgs)) : void 0);
519619
519738
  if (contracted.length > 0) {
519620
519739
  this.emit({
519621
519740
  type: "status",
519622
- content: `Phase contraction: ${contracted.join(", ")} → contracted (anchors retained)`,
519741
+ content: `Phase contraction: ${contracted.join(", ")} → contracted (${summarizer ? "LLM-summarized" : "stub-summarized"}, anchors retained)`,
519623
519742
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
519624
519743
  });
519625
519744
  }
519745
+ this._phaseMessageStartIdx = messages2.length;
519626
519746
  this._taskState.phase = transition.to;
519627
519747
  this._taskState.phaseSince = turn;
519628
519748
  }
@@ -584803,6 +584923,119 @@ ${incompleteList}${more}
584803
584923
  }
584804
584924
  };
584805
584925
  }
584926
+ function wireAgentToolMinimal(tool, config, repoRoot) {
584927
+ const typeRegistry = getAgentTypeRegistry();
584928
+ const allToolNames = [
584929
+ "file_read",
584930
+ "file_write",
584931
+ "file_edit",
584932
+ "shell",
584933
+ "grep",
584934
+ "glob",
584935
+ "list_directory",
584936
+ "web_fetch",
584937
+ "web_search",
584938
+ "memory_read",
584939
+ "memory_write",
584940
+ "task_complete",
584941
+ "batch_edit",
584942
+ "file_patch",
584943
+ "git_info",
584944
+ "agent",
584945
+ "send_message"
584946
+ ];
584947
+ tool.setCallbacks({
584948
+ resolveType: (typeName) => {
584949
+ const def = typeRegistry.get(typeName);
584950
+ if (!def) return void 0;
584951
+ const resolvedNames = typeRegistry.resolveTools(typeName, allToolNames);
584952
+ return {
584953
+ type: def.type,
584954
+ maxTurns: def.maxTurns,
584955
+ toolNames: resolvedNames,
584956
+ systemPromptAddition: def.systemPromptAddition,
584957
+ canSpawnAgents: def.canSpawnAgents
584958
+ };
584959
+ },
584960
+ listTypes: () => typeRegistry.listTypes(),
584961
+ spawnInProcess: async (opts) => {
584962
+ let backend;
584963
+ if (config.backendType === "nexus") {
584964
+ const nexusTool = new NexusTool(repoRoot);
584965
+ backend = new NexusAgenticBackend(nexusTool.sendCommand.bind(nexusTool), opts.model, void 0, config.apiKey);
584966
+ } else {
584967
+ backend = new OllamaAgenticBackend(config.backendUrl, opts.model, config.apiKey);
584968
+ }
584969
+ const subTier = getModelTier(opts.model);
584970
+ const subCompaction = subTier === "small" ? 12e3 : subTier === "medium" ? 24e3 : 4e4;
584971
+ const subRunner = new AgenticRunner(backend, {
584972
+ maxTurns: opts.maxTurns,
584973
+ maxTokens: 16384,
584974
+ temperature: 0,
584975
+ requestTimeoutMs: config.timeoutMs,
584976
+ taskTimeoutMs: config.timeoutMs * 2,
584977
+ compactionThreshold: subCompaction,
584978
+ contextWindowSize: 0,
584979
+ modelTier: subTier,
584980
+ subAgent: opts.subAgentMode === true
584981
+ });
584982
+ subRunner.setWorkingDirectory(repoRoot);
584983
+ const allSafe = buildSubAgentTools(repoRoot, config);
584984
+ const nameSet = new Set(opts.toolNames || []);
584985
+ const subToolInstances = nameSet.size > 0 ? allSafe.filter((t2) => nameSet.has(t2.name)) : allSafe.slice();
584986
+ const nameOf = (t2) => t2.name;
584987
+ if (!subToolInstances.some((t2) => nameOf(t2) === "todo_write")) subToolInstances.push(new TodoWriteTool());
584988
+ if (!subToolInstances.some((t2) => nameOf(t2) === "todo_read")) subToolInstances.push(new TodoReadTool());
584989
+ subRunner.registerTools(subToolInstances.map(adaptTool6));
584990
+ subRunner.registerTool(createTaskCompleteTool(subTier));
584991
+ const systemCtx = opts.systemPromptAddition ? `Working directory: ${repoRoot}
584992
+
584993
+ ${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
584994
+ const result = await subRunner.run(opts.task, systemCtx);
584995
+ const subState = subRunner.getTaskState();
584996
+ const filesModified = Array.from(subState.modifiedFiles.keys());
584997
+ const artifactIds = [];
584998
+ if (_parentRunnerForArchive && result.summary && result.summary.length > 0) {
584999
+ const artifactId = `subagent-${opts.id}-result`;
585000
+ try {
585001
+ _parentRunnerForArchive.archiveToMemex({
585002
+ id: artifactId,
585003
+ toolName: "agent",
585004
+ summary: `Sub-agent ${opts.id}: ${result.summary.slice(0, 120)}`,
585005
+ fullContent: `Sub-agent task: ${opts.task.slice(0, 1e3)}
585006
+
585007
+ Completed: ${result.completed}
585008
+ Turns: ${result.turns}, Tool calls: ${result.toolCalls}
585009
+ Files modified: ${filesModified.join(", ") || "(none)"}
585010
+
585011
+ Summary:
585012
+ ${result.summary}`
585013
+ });
585014
+ artifactIds.push(artifactId);
585015
+ } catch {
585016
+ }
585017
+ }
585018
+ return {
585019
+ completed: result.completed,
585020
+ summary: result.summary,
585021
+ turns: result.turns,
585022
+ toolCalls: result.toolCalls,
585023
+ durationMs: result.durationMs,
585024
+ filesModified,
585025
+ artifactIds
585026
+ };
585027
+ },
585028
+ spawnSubprocess: (opts) => {
585029
+ const entry = spawnFullSubAgent(
585030
+ opts.task,
585031
+ { model: opts.model, backendUrl: config.backendUrl, workingDir: opts.workingDir }
585032
+ );
585033
+ return { id: entry.id, pid: entry.pid ?? 0 };
585034
+ },
585035
+ trackTask: () => {
585036
+ }
585037
+ });
585038
+ }
584806
585039
  function buildSubAgentTools(repoRoot, config) {
584807
585040
  return [
584808
585041
  // File + search
@@ -585684,7 +585917,8 @@ RULES:
585684
585917
  }
585685
585918
  } catch {
585686
585919
  }
585687
- const compactionThreshold = modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
585920
+ const envOverride = Number.parseInt(process.env["OA_COMPACTION_THRESHOLD"] ?? "", 10);
585921
+ const compactionThreshold = Number.isFinite(envOverride) && envOverride > 0 ? envOverride : modelTier === "small" ? 12e3 : modelTier === "medium" ? 24e3 : 4e4;
585688
585922
  let identityInjection = "";
585689
585923
  try {
585690
585924
  const ikStateFile = join109(repoRoot, ".oa", "identity", "self-state.json");
@@ -585732,6 +585966,7 @@ RULES:
585732
585966
  });
585733
585967
  runner.setWorkingDirectory(repoRoot);
585734
585968
  _activeRunnerRef = runner;
585969
+ _parentRunnerForArchive = runner;
585735
585970
  let _checkinPoller = null;
585736
585971
  try {
585737
585972
  const oaSessionId = process.env["OA_SESSION_ID"];
@@ -587218,12 +587453,38 @@ Review its full output in the [${id}] tab or via sub_agent(action='output', id='
587218
587453
 
587219
587454
  ${opts.systemPromptAddition}` : `Working directory: ${repoRoot}`;
587220
587455
  const result = await subRunner.run(opts.task, systemCtx);
587456
+ const subState = subRunner.getTaskState();
587457
+ const filesModified = Array.from(subState.modifiedFiles.keys());
587458
+ const artifactIds = [];
587459
+ if (_parentRunnerForArchive && result.summary && result.summary.length > 0) {
587460
+ const artifactId = `subagent-${opts.id}-result`;
587461
+ try {
587462
+ _parentRunnerForArchive.archiveToMemex({
587463
+ id: artifactId,
587464
+ toolName: "agent",
587465
+ summary: `Sub-agent ${opts.id}: ${result.summary.slice(0, 120)}`,
587466
+ fullContent: `Sub-agent task: ${opts.task.slice(0, 1e3)}
587467
+
587468
+ Completed: ${result.completed}
587469
+ Turns: ${result.turns}, Tool calls: ${result.toolCalls}
587470
+ Duration: ${result.durationMs}ms
587471
+ Files modified: ${filesModified.join(", ") || "(none)"}
587472
+
587473
+ Summary:
587474
+ ${result.summary}`
587475
+ });
587476
+ artifactIds.push(artifactId);
587477
+ } catch {
587478
+ }
587479
+ }
587221
587480
  return {
587222
587481
  completed: result.completed,
587223
587482
  summary: result.summary,
587224
587483
  turns: result.turns,
587225
587484
  toolCalls: result.toolCalls,
587226
- durationMs: result.durationMs
587485
+ durationMs: result.durationMs,
587486
+ filesModified,
587487
+ artifactIds
587227
587488
  };
587228
587489
  },
587229
587490
  spawnSubprocess: (opts) => {
@@ -590841,6 +591102,9 @@ async function runWithTUI(task, config, repoPath, callbacks) {
590841
591102
  renderCompactHeader(config.model);
590842
591103
  renderUserMessage(task);
590843
591104
  setTerminalTitle(task.slice(0, 60), getVersion4());
591105
+ if (!_wireAgentToolCallbacks) {
591106
+ _wireAgentToolCallbacks = (tool) => wireAgentToolMinimal(tool, config, repoRoot);
591107
+ }
590844
591108
  try {
590845
591109
  const handle2 = startTask(task, config, repoRoot);
590846
591110
  await handle2.promise;
@@ -591106,7 +591370,7 @@ Rules:
591106
591370
  process.exit(1);
591107
591371
  }
591108
591372
  }
591109
- var _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
591373
+ var _interactiveSessionActive, _interactiveSessionReason, _voiceChatSession, taskManager, _apiCallbacks, _shellToolRef, _replToolRef, _fullSubAgentToolRef, _agentToolRef, _sendMessageToolRef, _agentLifecycleMgr, _activeRunnerRef, _parentRunnerForArchive, _wireSubAgentCallbacks, _wireAgentToolCallbacks, _wireSubAgentToolCallbacks, _autoUpdatedThisSession, _mcpManager, _pluginManager, _mcpTools, SELF_IMPROVE_INTERVAL, _tasksSinceImprove;
591110
591374
  var init_interactive = __esm({
591111
591375
  "packages/cli/src/tui/interactive.ts"() {
591112
591376
  "use strict";
@@ -591164,6 +591428,7 @@ var init_interactive = __esm({
591164
591428
  _sendMessageToolRef = null;
591165
591429
  _agentLifecycleMgr = getSharedTaskManager();
591166
591430
  _activeRunnerRef = null;
591431
+ _parentRunnerForArchive = null;
591167
591432
  _wireSubAgentCallbacks = null;
591168
591433
  _wireAgentToolCallbacks = null;
591169
591434
  _wireSubAgentToolCallbacks = null;
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.449",
3
+ "version": "0.187.450",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "open-agents-ai",
9
- "version": "0.187.449",
9
+ "version": "0.187.450",
10
10
  "hasInstallScript": true,
11
11
  "license": "CC-BY-NC-4.0",
12
12
  "dependencies": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.449",
3
+ "version": "0.187.450",
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",