open-agents-ai 0.187.179 → 0.187.181

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 +188 -147
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -284371,6 +284371,7 @@ var oa_directory_exports = {};
284371
284371
  __export(oa_directory_exports, {
284372
284372
  OA_DIR: () => OA_DIR,
284373
284373
  buildContextRestorePrompt: () => buildContextRestorePrompt,
284374
+ cleanPromptForDiary: () => cleanPromptForDiary,
284374
284375
  deleteSession: () => deleteSession,
284375
284376
  deleteUsageRecord: () => deleteUsageRecord,
284376
284377
  discoverContextFiles: () => discoverContextFiles,
@@ -284389,6 +284390,7 @@ __export(oa_directory_exports, {
284389
284390
  readIndexData: () => readIndexData,
284390
284391
  readIndexMeta: () => readIndexMeta,
284391
284392
  recordUsage: () => recordUsage,
284393
+ renderSessionDiary: () => renderSessionDiary,
284392
284394
  resolveSettings: () => resolveSettings,
284393
284395
  saveGlobalSettings: () => saveGlobalSettings,
284394
284396
  savePendingTask: () => savePendingTask,
@@ -284671,22 +284673,59 @@ function saveSessionContext(repoRoot, entry) {
284671
284673
  ctx3.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
284672
284674
  writeFileSync26(filePath, JSON.stringify(ctx3, null, 2) + "\n", "utf-8");
284673
284675
  try {
284674
- const diaryLines = ["# Session Diary", ""];
284675
- for (const e2 of ctx3.entries.slice(-10)) {
284676
- const date = e2.savedAt ? new Date(e2.savedAt).toISOString().slice(0, 16).replace("T", " ") : "unknown";
284677
- const status = e2.completed ? "\u2713" : "\u25CB";
284678
- const task = (e2.task || "untitled").slice(0, 120);
284679
- diaryLines.push(`## ${date} \u2014 ${task} ${status}`);
284680
- if (e2.filesModified?.length) {
284681
- diaryLines.push(`Files: ${e2.filesModified.slice(0, 5).join(", ")}${e2.filesModified.length > 5 ? ` (+${e2.filesModified.length - 5} more)` : ""}`);
284682
- }
284683
- if (e2.summary) diaryLines.push(`Summary: ${e2.summary.slice(0, 200)}`);
284684
- diaryLines.push("");
284685
- }
284686
- writeFileSync26(join71(contextDir, "session-diary.md"), diaryLines.join("\n"), "utf-8");
284676
+ writeFileSync26(
284677
+ join71(contextDir, "session-diary.md"),
284678
+ renderSessionDiary(ctx3.entries.slice(-10)),
284679
+ "utf-8"
284680
+ );
284687
284681
  } catch {
284688
284682
  }
284689
284683
  }
284684
+ function cleanPromptForDiary(raw) {
284685
+ if (!raw) return "";
284686
+ let text = String(raw);
284687
+ text = text.replace(/^\[Previous sessions exist[^\]]*\]\s*\n*/m, "");
284688
+ text = text.replace(/^[\s\S]*?\n---\s*\n\s*NEW TASK:\s*/m, "");
284689
+ text = text.replace(/^NEW TASK:\s*/m, "");
284690
+ text = text.replace(/^<system>[\s\S]*?<\/system>\s*\n*/m, "");
284691
+ text = text.replace(/\s+/g, " ").trim();
284692
+ return text || "(empty prompt)";
284693
+ }
284694
+ function renderSessionDiary(entries) {
284695
+ const lines = ["# Session Diary", ""];
284696
+ if (entries.length === 0) {
284697
+ lines.push("_(no sessions recorded yet)_", "");
284698
+ return lines.join("\n");
284699
+ }
284700
+ for (const e2 of entries) {
284701
+ const date = e2.savedAt ? new Date(e2.savedAt).toISOString().slice(0, 16).replace("T", " ") : "unknown time";
284702
+ const status = e2.completed ? "\u2713" : "\u25CB";
284703
+ const cleanPrompt = cleanPromptForDiary(e2.task);
284704
+ const slug = cleanPrompt.length > 80 ? cleanPrompt.slice(0, 77) + "..." : cleanPrompt;
284705
+ lines.push(`## ${date} ${status} ${slug || "untitled"}`);
284706
+ lines.push("");
284707
+ lines.push(`- **Prompt:** ${cleanPrompt || "(empty prompt)"}`);
284708
+ if (e2.model) {
284709
+ lines.push(`- **Model:** ${e2.model}`);
284710
+ }
284711
+ if (e2.filesModified && e2.filesModified.length > 0) {
284712
+ const shown = e2.filesModified.slice(0, 5).join(", ");
284713
+ const more = e2.filesModified.length > 5 ? ` (+${e2.filesModified.length - 5} more)` : "";
284714
+ lines.push(`- **Files:** ${shown}${more}`);
284715
+ }
284716
+ if (typeof e2.toolCalls === "number" && e2.toolCalls > 0) {
284717
+ lines.push(`- **Tools:** ${e2.toolCalls} call${e2.toolCalls === 1 ? "" : "s"}`);
284718
+ }
284719
+ if (e2.summary) {
284720
+ const summary = e2.summary.replace(/\s+/g, " ").trim().slice(0, 280);
284721
+ lines.push(`- **Summary:** ${summary}`);
284722
+ }
284723
+ lines.push("");
284724
+ }
284725
+ while (lines.length > 1 && lines[lines.length - 1] === "") lines.pop();
284726
+ lines.push("");
284727
+ return lines.join("\n");
284728
+ }
284690
284729
  function loadSessionContext(repoRoot) {
284691
284730
  const filePath = join71(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
284692
284731
  try {
@@ -302317,6 +302356,13 @@ async function handleMcp(subcommand, ctx3) {
302317
302356
  }
302318
302357
  await showMcpMainMenu(ctx3);
302319
302358
  }
302359
+ function validateMcpConfigShape(parsed) {
302360
+ if (!parsed || typeof parsed !== "object") return "Config must be a JSON object";
302361
+ if (typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302362
+ return "Config must have either 'command' (stdio) or 'url' (http)";
302363
+ }
302364
+ return null;
302365
+ }
302320
302366
  async function showMcpMainMenu(ctx3) {
302321
302367
  const manager = ctx3.getMcpManager?.();
302322
302368
  if (!manager) return;
@@ -302354,19 +302400,76 @@ async function showMcpMainMenu(ctx3) {
302354
302400
  }
302355
302401
  items.push({ key: "hdr_actions", label: selectColors.dim("\u2500\u2500\u2500 Actions \u2500\u2500\u2500"), kind: "header" });
302356
302402
  skipKeys.push("hdr_actions");
302357
- items.push({ key: "act_add", label: c3.cyan("\u2795 Add new server"), detail: "Paste/enter a config", kind: "action" });
302403
+ items.push({ key: "act_add", label: c3.cyan("\u2795 Add new server"), detail: "Inline name + JSON intake", kind: "action" });
302358
302404
  items.push({ key: "act_reload", label: c3.cyan("\u27F3 Reload all from config"), detail: "Re-read .oa/mcp.json + reconnect", kind: "action" });
302359
- items.push({ key: "act_open", label: c3.cyan("\u{1F4DD} Open .oa/mcp.json in editor"), detail: "External edit", kind: "action" });
302405
+ items.push({ key: "act_open", label: c3.cyan("\u{1F4DD} Show .oa/mcp.json path"), detail: "For external editing", kind: "action" });
302406
+ let pendingAdd = null;
302407
+ const postMessages = [];
302360
302408
  const result = await tuiSelect({
302361
302409
  items,
302362
302410
  title: "MCP Server Registry",
302363
- skipKeys
302411
+ skipKeys,
302412
+ onEnter: (item, helpers) => {
302413
+ if (item.key === "act_add") {
302414
+ helpers.getInput("Server name (e.g. memory):", "").then(async (name10) => {
302415
+ if (!name10 || name10.trim().length === 0) {
302416
+ postMessages.push({ kind: "info", text: "Add cancelled \u2014 no name entered." });
302417
+ helpers.render();
302418
+ return;
302419
+ }
302420
+ const cfgStr = await helpers.getInput(
302421
+ 'Config JSON (e.g. {"command":"npx","args":["-y","@modelcontextprotocol/server-memory"]}):',
302422
+ ""
302423
+ );
302424
+ if (!cfgStr || cfgStr.trim().length === 0) {
302425
+ postMessages.push({ kind: "info", text: "Add cancelled \u2014 no config entered." });
302426
+ helpers.render();
302427
+ return;
302428
+ }
302429
+ let parsed;
302430
+ try {
302431
+ parsed = JSON.parse(cfgStr);
302432
+ } catch (e2) {
302433
+ postMessages.push({ kind: "error", text: `Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}` });
302434
+ helpers.render();
302435
+ return;
302436
+ }
302437
+ const shapeErr = validateMcpConfigShape(parsed);
302438
+ if (shapeErr) {
302439
+ postMessages.push({ kind: "error", text: shapeErr });
302440
+ helpers.render();
302441
+ return;
302442
+ }
302443
+ pendingAdd = { name: name10.trim(), config: parsed };
302444
+ helpers.resolve({ confirmed: true, key: "__pending_add__", index: -1 });
302445
+ });
302446
+ return true;
302447
+ }
302448
+ return false;
302449
+ }
302364
302450
  });
302365
- if (!result || !result.confirmed || !result.key) return;
302366
- if (result.key === "act_add") {
302367
- await mcpAddServerInteractive(ctx3);
302451
+ for (const msg of postMessages) {
302452
+ if (msg.kind === "info") renderInfo(msg.text);
302453
+ else if (msg.kind === "warn") renderWarning(msg.text);
302454
+ else renderError(msg.text);
302455
+ }
302456
+ if (pendingAdd) {
302457
+ const { name: name10, config } = pendingAdd;
302458
+ try {
302459
+ const c22 = await manager.addServer(name10, config);
302460
+ await ctx3.refreshMcpTools?.();
302461
+ if (c22.status === "connected") {
302462
+ renderInfo(`\u2714 ${name10} added and connected (${c22.tools.length} tools)`);
302463
+ } else {
302464
+ renderWarning(`\u26A0 ${name10} saved to .oa/mcp.json but not connected: ${c22.error ?? "unknown"}`);
302465
+ }
302466
+ } catch (e2) {
302467
+ renderError(`Add failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302468
+ }
302469
+ await showMcpMainMenu(ctx3);
302368
302470
  return;
302369
302471
  }
302472
+ if (!result || !result.confirmed || !result.key) return;
302370
302473
  if (result.key === "act_reload") {
302371
302474
  renderInfo("Reloading MCP servers...");
302372
302475
  await manager.disconnectAll().catch(() => {
@@ -302450,15 +302553,76 @@ async function showMcpDetailMenu(ctx3, serverName) {
302450
302553
  }
302451
302554
  items.push({ key: "hdr_actions", label: selectColors.dim("\u2500\u2500\u2500 Actions \u2500\u2500\u2500"), kind: "header" });
302452
302555
  skipKeys.push("hdr_actions");
302453
- items.push({ key: "act_edit", label: c3.cyan("\u270E Edit config"), detail: "Modify command/args/env/url/headers", kind: "action" });
302556
+ items.push({ key: "act_edit", label: c3.cyan("\u270E Edit config"), detail: "Modify command/args/env/url/headers (inline)", kind: "action" });
302454
302557
  items.push({ key: "act_reconnect", label: c3.cyan("\u27F3 Reconnect"), detail: "Disconnect + reconnect this server", kind: "action" });
302455
302558
  items.push({ key: "act_delete", label: c3.red("\u{1F5D1} Delete"), detail: "Remove from config + disconnect", kind: "action" });
302456
302559
  items.push({ key: "act_back", label: c3.dim("\u2190 Back to server list"), kind: "action" });
302560
+ let pendingEdit = null;
302561
+ const postMessages = [];
302457
302562
  const result = await tuiSelect({
302458
302563
  items,
302459
302564
  title: `MCP: ${serverName}`,
302460
- skipKeys
302565
+ skipKeys,
302566
+ onEnter: (item, helpers) => {
302567
+ if (item.key === "act_edit") {
302568
+ const currentJson = JSON.stringify(persisted ?? {});
302569
+ helpers.getInput(
302570
+ `New JSON config for ${serverName} (current shown \u2014 edit then Enter; Esc to cancel):`,
302571
+ currentJson
302572
+ ).then(async (newJson) => {
302573
+ if (newJson === null || newJson.trim().length === 0) {
302574
+ postMessages.push({ kind: "info", text: "Edit cancelled \u2014 no change." });
302575
+ helpers.render();
302576
+ return;
302577
+ }
302578
+ if (newJson.trim() === currentJson.trim()) {
302579
+ postMessages.push({ kind: "info", text: "Edit cancelled \u2014 config unchanged." });
302580
+ helpers.render();
302581
+ return;
302582
+ }
302583
+ let parsed;
302584
+ try {
302585
+ parsed = JSON.parse(newJson);
302586
+ } catch (e2) {
302587
+ postMessages.push({ kind: "error", text: `Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}` });
302588
+ helpers.render();
302589
+ return;
302590
+ }
302591
+ const shapeErr = validateMcpConfigShape(parsed);
302592
+ if (shapeErr) {
302593
+ postMessages.push({ kind: "error", text: shapeErr });
302594
+ helpers.render();
302595
+ return;
302596
+ }
302597
+ pendingEdit = parsed;
302598
+ helpers.resolve({ confirmed: true, key: "__pending_edit__", index: -1 });
302599
+ });
302600
+ return true;
302601
+ }
302602
+ return false;
302603
+ }
302461
302604
  });
302605
+ for (const msg of postMessages) {
302606
+ if (msg.kind === "info") renderInfo(msg.text);
302607
+ else if (msg.kind === "warn") renderWarning(msg.text);
302608
+ else renderError(msg.text);
302609
+ }
302610
+ if (pendingEdit) {
302611
+ try {
302612
+ await manager.disconnect(serverName);
302613
+ const c22 = await manager.addServer(serverName, pendingEdit);
302614
+ await ctx3.refreshMcpTools?.();
302615
+ if (c22.status === "connected") {
302616
+ renderInfo(`\u2714 ${serverName} updated and reconnected (${c22.tools.length} tools)`);
302617
+ } else {
302618
+ renderWarning(`\u26A0 ${serverName} saved but not connected: ${c22.error ?? "unknown"}`);
302619
+ }
302620
+ } catch (e2) {
302621
+ renderError(`Save failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302622
+ }
302623
+ await showMcpDetailMenu(ctx3, serverName);
302624
+ return;
302625
+ }
302462
302626
  if (!result || !result.confirmed || !result.key) {
302463
302627
  await showMcpMainMenu(ctx3);
302464
302628
  return;
@@ -302467,10 +302631,6 @@ async function showMcpDetailMenu(ctx3, serverName) {
302467
302631
  await showMcpMainMenu(ctx3);
302468
302632
  return;
302469
302633
  }
302470
- if (result.key === "act_edit") {
302471
- await mcpEditServer(ctx3, serverName, persisted);
302472
- return;
302473
- }
302474
302634
  if (result.key === "act_reconnect") {
302475
302635
  renderInfo(`Reconnecting ${serverName}...`);
302476
302636
  try {
@@ -302503,126 +302663,6 @@ async function showMcpDetailMenu(ctx3, serverName) {
302503
302663
  return;
302504
302664
  }
302505
302665
  }
302506
- async function mcpEditServer(ctx3, serverName, current) {
302507
- const manager = ctx3.getMcpManager?.();
302508
- if (!manager) return;
302509
- const ask2 = (prompt) => new Promise((res) => {
302510
- process.stdout.write(prompt);
302511
- let buf = "";
302512
- const onData = (d2) => {
302513
- const s2 = d2.toString();
302514
- if (s2.includes("\n")) {
302515
- process.stdin.removeListener("data", onData);
302516
- process.stdin.pause();
302517
- res((buf + s2).split("\n")[0]);
302518
- } else {
302519
- buf += s2;
302520
- }
302521
- };
302522
- process.stdin.resume();
302523
- process.stdin.on("data", onData);
302524
- });
302525
- process.stdout.write(`
302526
- ${c3.cyan("Current config for ")}${c3.bold(serverName)}:
302527
- `);
302528
- process.stdout.write(` ${c3.dim(JSON.stringify(current ?? {}, null, 2).split("\n").join("\n "))}
302529
-
302530
- `);
302531
- process.stdout.write(` ${c3.cyan("Paste a new JSON config (single line) or press Enter to keep current.")}
302532
- `);
302533
- const newConfigJson = await ask2(" > ");
302534
- if (!newConfigJson || newConfigJson.trim().length === 0) {
302535
- renderInfo("No change.");
302536
- await showMcpDetailMenu(ctx3, serverName);
302537
- return;
302538
- }
302539
- let parsed;
302540
- try {
302541
- parsed = JSON.parse(newConfigJson);
302542
- } catch (e2) {
302543
- renderError(`Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}`);
302544
- await showMcpDetailMenu(ctx3, serverName);
302545
- return;
302546
- }
302547
- if (!parsed || typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302548
- renderError("Config must have either 'command' (stdio) or 'url' (http)");
302549
- await showMcpDetailMenu(ctx3, serverName);
302550
- return;
302551
- }
302552
- try {
302553
- await manager.disconnect(serverName);
302554
- const c22 = await manager.addServer(serverName, parsed);
302555
- await ctx3.refreshMcpTools?.();
302556
- if (c22.status === "connected") {
302557
- renderInfo(`\u2714 ${serverName} updated and reconnected (${c22.tools.length} tools)`);
302558
- } else {
302559
- renderWarning(`\u26A0 ${serverName} saved but not connected: ${c22.error}`);
302560
- }
302561
- } catch (e2) {
302562
- renderError(`Save failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302563
- }
302564
- await showMcpMainMenu(ctx3);
302565
- }
302566
- async function mcpAddServerInteractive(ctx3) {
302567
- const ask2 = (prompt) => new Promise((res) => {
302568
- process.stdout.write(prompt);
302569
- let buf = "";
302570
- const onData = (d2) => {
302571
- const s2 = d2.toString();
302572
- if (s2.includes("\n")) {
302573
- process.stdin.removeListener("data", onData);
302574
- process.stdin.pause();
302575
- res((buf + s2).split("\n")[0]);
302576
- } else {
302577
- buf += s2;
302578
- }
302579
- };
302580
- process.stdin.resume();
302581
- process.stdin.on("data", onData);
302582
- });
302583
- process.stdout.write(`
302584
- ${c3.cyan("Add MCP server")}
302585
- `);
302586
- process.stdout.write(` ${c3.dim("Tip: drag-drop a markdown file with an MCP config block onto the terminal for auto-intake.")}
302587
-
302588
- `);
302589
- const name10 = await ask2(` ${c3.cyan("Server name:")} `);
302590
- if (!name10 || name10.trim().length === 0) {
302591
- renderInfo("Cancelled.");
302592
- return;
302593
- }
302594
- process.stdout.write(` ${c3.cyan('Config JSON (one line, e.g. {"command":"npx","args":["-y","@modelcontextprotocol/server-memory"]}):')}
302595
- `);
302596
- const cfgStr = await ask2(" > ");
302597
- let parsed;
302598
- try {
302599
- parsed = JSON.parse(cfgStr);
302600
- } catch (e2) {
302601
- renderError(`Invalid JSON: ${e2 instanceof Error ? e2.message : String(e2)}`);
302602
- return;
302603
- }
302604
- if (!parsed || typeof parsed.command !== "string" && typeof parsed.url !== "string") {
302605
- renderError("Config must have either 'command' (stdio) or 'url' (http)");
302606
- return;
302607
- }
302608
- const manager = ctx3.getMcpManager?.();
302609
- if (!manager) {
302610
- renderError("MCP manager not available");
302611
- return;
302612
- }
302613
- try {
302614
- const c22 = await manager.addServer(name10.trim(), parsed);
302615
- await ctx3.refreshMcpTools?.();
302616
- if (c22.status === "connected") {
302617
- renderInfo(`\u2714 ${name10.trim()} added and connected (${c22.tools.length} tools)`);
302618
- } else {
302619
- renderWarning(`\u26A0 ${name10.trim()} saved but not connected: ${c22.error}`);
302620
- }
302621
- } catch (e2) {
302622
- renderError(`Add failed: ${e2 instanceof Error ? e2.message : String(e2)}`);
302623
- }
302624
- await showMcpMainMenu(ctx3);
302625
- }
302626
302666
  async function handleUpdate(subcommand, ctx3) {
302627
302667
  const repoRoot = ctx3.repoRoot;
302628
302668
  if (subcommand === "auto") {
@@ -316645,7 +316685,7 @@ When done, either call task_complete with your answer, or use FINAL_VAR(variable
316645
316685
  try {
316646
316686
  saveSessionContext(repoRoot, {
316647
316687
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
316648
- task: task.slice(0, 500),
316688
+ task: cleanPromptForDiary(task).slice(0, 500),
316649
316689
  summary: result.summary.slice(0, 500),
316650
316690
  filesModified: Array.from(filesTouched).slice(0, 30),
316651
316691
  toolCalls: result.toolCalls,
@@ -319362,9 +319402,10 @@ Respond concisely and safely. Remember: you are talking to the general public.`;
319362
319402
  },
319363
319403
  contextSave() {
319364
319404
  try {
319405
+ const cleaned = (lastSubmittedPrompt || "(manual save)").replace(/^\[Previous sessions exist[^\]]*\]\s*/m, "").replace(/^NEW TASK:\s*/m, "").trim() || "(manual save)";
319365
319406
  const entry = {
319366
319407
  savedAt: (/* @__PURE__ */ new Date()).toISOString(),
319367
- task: lastSubmittedPrompt || "(manual save)",
319408
+ task: cleaned,
319368
319409
  summary: `Manual context save. ${sessionToolCallCount} tool calls, ${sessionFilesTouched.length} files modified.`,
319369
319410
  filesModified: [...sessionFilesTouched],
319370
319411
  toolCalls: sessionToolCallCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.179",
3
+ "version": "0.187.181",
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",