open-agents-ai 0.31.7 → 0.31.9

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 +165 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -15002,6 +15002,9 @@ function renderSlashHelp() {
15002
15002
  ["/stop", "Kill current inference immediately and save state (/resume to continue)"],
15003
15003
  ["/resume", "Resume a paused or stopped task"],
15004
15004
  ["/destroy", "Remove .oa folder, kill all tasks, clear console, and exit"],
15005
+ ["/context save", "Force-save session context to .oa/context/"],
15006
+ ["/context restore", "Restore context from previous sessions into next task"],
15007
+ ["/context show", "Show saved session context status"],
15005
15008
  ["/compact", "Force context compaction now (default strategy)"],
15006
15009
  ["/compact <strategy>", "Compact with strategy: aggressive, decisions, errors, summary, structured"],
15007
15010
  ["/bruteforce", "Toggle brute-force mode (auto re-engage on turn limit)"],
@@ -15793,6 +15796,61 @@ function loadPendingTask(repoRoot) {
15793
15796
  return null;
15794
15797
  }
15795
15798
  }
15799
+ function saveSessionContext(repoRoot, entry) {
15800
+ const contextDir = join24(repoRoot, OA_DIR, "context");
15801
+ mkdirSync6(contextDir, { recursive: true });
15802
+ const filePath = join24(contextDir, CONTEXT_SAVE_FILE);
15803
+ let ctx;
15804
+ try {
15805
+ if (existsSync18(filePath)) {
15806
+ ctx = JSON.parse(readFileSync13(filePath, "utf-8"));
15807
+ } else {
15808
+ ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
15809
+ }
15810
+ } catch {
15811
+ ctx = { entries: [], maxEntries: MAX_CONTEXT_ENTRIES, updatedAt: "" };
15812
+ }
15813
+ ctx.entries.push(entry);
15814
+ if (ctx.entries.length > ctx.maxEntries) {
15815
+ ctx.entries = ctx.entries.slice(-ctx.maxEntries);
15816
+ }
15817
+ ctx.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
15818
+ writeFileSync6(filePath, JSON.stringify(ctx, null, 2) + "\n", "utf-8");
15819
+ }
15820
+ function loadSessionContext(repoRoot) {
15821
+ const filePath = join24(repoRoot, OA_DIR, "context", CONTEXT_SAVE_FILE);
15822
+ try {
15823
+ if (!existsSync18(filePath))
15824
+ return null;
15825
+ return JSON.parse(readFileSync13(filePath, "utf-8"));
15826
+ } catch {
15827
+ return null;
15828
+ }
15829
+ }
15830
+ function buildContextRestorePrompt(repoRoot) {
15831
+ const ctx = loadSessionContext(repoRoot);
15832
+ if (!ctx || ctx.entries.length === 0)
15833
+ return null;
15834
+ const lines = [
15835
+ "[RESTORED SESSION CONTEXT] Previous session history for this project:",
15836
+ ""
15837
+ ];
15838
+ const recentEntries = ctx.entries.slice(-10);
15839
+ for (const entry of recentEntries) {
15840
+ const status = entry.completed ? "COMPLETED" : "INCOMPLETE";
15841
+ const date = new Date(entry.savedAt).toLocaleString();
15842
+ lines.push(`[${status}] ${date} \u2014 ${entry.task.slice(0, 200)}`);
15843
+ if (entry.summary) {
15844
+ lines.push(` Summary: ${entry.summary.slice(0, 300)}`);
15845
+ }
15846
+ if (entry.filesModified.length > 0) {
15847
+ lines.push(` Files: ${entry.filesModified.slice(0, 10).join(", ")}`);
15848
+ }
15849
+ lines.push("");
15850
+ }
15851
+ lines.push("Use this context to understand what has been done previously. Do not repeat completed work.");
15852
+ return lines.join("\n");
15853
+ }
15796
15854
  function detectManifests(repoRoot) {
15797
15855
  const manifests = [];
15798
15856
  const checks = [
@@ -15886,7 +15944,7 @@ function buildDirTree(root, maxDepth, prefix = "", depth = 0) {
15886
15944
  }
15887
15945
  return result;
15888
15946
  }
15889
- var OA_DIR, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, SKIP_DIRS;
15947
+ var OA_DIR, SUBDIRS, CONTEXT_FILES, PENDING_TASK_FILE, CONTEXT_SAVE_FILE, MAX_CONTEXT_ENTRIES, SKIP_DIRS;
15890
15948
  var init_oa_directory = __esm({
15891
15949
  "packages/cli/dist/tui/oa-directory.js"() {
15892
15950
  "use strict";
@@ -15903,6 +15961,8 @@ var init_oa_directory = __esm({
15903
15961
  "AGENTS.md"
15904
15962
  ];
15905
15963
  PENDING_TASK_FILE = "pending-task.json";
15964
+ CONTEXT_SAVE_FILE = "session-context.json";
15965
+ MAX_CONTEXT_ENTRIES = 20;
15906
15966
  SKIP_DIRS = /* @__PURE__ */ new Set([
15907
15967
  "node_modules",
15908
15968
  ".git",
@@ -17502,6 +17562,53 @@ async function handleSlashCommand(input, ctx) {
17502
17562
  ctx.destroyProject?.();
17503
17563
  return "exit";
17504
17564
  }
17565
+ case "context": {
17566
+ const subCmd = arg?.toLowerCase().split(/\s+/)[0] || "";
17567
+ switch (subCmd) {
17568
+ case "save": {
17569
+ const ok = ctx.contextSave?.() ?? false;
17570
+ if (ok) {
17571
+ renderInfo("Session context saved to .oa/context/. Will auto-restore on next /context restore.");
17572
+ } else {
17573
+ renderWarning("Could not save session context.");
17574
+ }
17575
+ break;
17576
+ }
17577
+ case "restore": {
17578
+ if (ctx.hasActiveTask?.()) {
17579
+ renderWarning("Cannot restore context while a task is running.");
17580
+ break;
17581
+ }
17582
+ const prompt = ctx.contextRestore?.();
17583
+ if (prompt) {
17584
+ const info = ctx.contextShow?.();
17585
+ renderInfo(`Context restored from ${info?.entries ?? 0} saved session(s). Will be injected into your next task.`);
17586
+ ctx.setRestoredContext?.(prompt);
17587
+ } else {
17588
+ renderWarning("No saved session context found. Complete a task first, or use /context save.");
17589
+ }
17590
+ break;
17591
+ }
17592
+ case "show":
17593
+ case "status": {
17594
+ const info = ctx.contextShow?.();
17595
+ if (info && info.entries > 0) {
17596
+ renderInfo(`Session context: ${info.entries} entries saved. Last saved: ${info.lastSaved ? new Date(info.lastSaved).toLocaleString() : "unknown"}`);
17597
+ } else {
17598
+ renderInfo("No session context saved yet. Context auto-saves on task completion.");
17599
+ }
17600
+ break;
17601
+ }
17602
+ default:
17603
+ renderInfo("Usage: /context save | /context restore | /context show");
17604
+ renderInfo(" save \u2014 Force-save current session context");
17605
+ renderInfo(" restore \u2014 Load previous session context into next task");
17606
+ renderInfo(" show \u2014 Show saved context status");
17607
+ renderInfo("Context auto-saves on every task completion.");
17608
+ break;
17609
+ }
17610
+ return "handled";
17611
+ }
17505
17612
  case "compact":
17506
17613
  case "gc": {
17507
17614
  if (!ctx.hasActiveTask?.()) {
@@ -17726,6 +17833,7 @@ async function handleUpdate(subcommand, ctx) {
17726
17833
  process.stdout.write(` ${c2.green("\u2714")} Installed v${info.latestVersion}. Reloading...
17727
17834
 
17728
17835
  `);
17836
+ ctx.contextSave?.();
17729
17837
  const hadActiveTask = ctx.savePendingTaskState?.() ?? false;
17730
17838
  const resumeFlag = hadActiveTask ? "1" : "update-only";
17731
17839
  const { execPath, argv } = process;
@@ -22340,6 +22448,18 @@ ${entry.fullContent}`
22340
22448
  });
22341
22449
  } catch {
22342
22450
  }
22451
+ try {
22452
+ saveSessionContext(repoRoot, {
22453
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
22454
+ task: task.slice(0, 500),
22455
+ summary: result.summary.slice(0, 500),
22456
+ filesModified: Array.from(filesTouched).slice(0, 30),
22457
+ toolCalls: result.toolCalls,
22458
+ completed: result.completed,
22459
+ model: config.model
22460
+ });
22461
+ } catch {
22462
+ }
22343
22463
  if (taskStores?.taskMemoryStore) {
22344
22464
  try {
22345
22465
  taskStores.taskMemoryStore.insert({
@@ -22571,6 +22691,7 @@ async function startInteractive(config, repoPath) {
22571
22691
  let currentTaskType;
22572
22692
  let sessionFilesTouched = [];
22573
22693
  let sessionToolCallCount = 0;
22694
+ let restoredSessionContext = null;
22574
22695
  let sessionSudoPassword = null;
22575
22696
  let sudoPromptPending = false;
22576
22697
  const idlePrompt = `${c2.bold(c2.white("\u276F "))}`;
@@ -22962,6 +23083,36 @@ async function startInteractive(config, repoPath) {
22962
23083
  writeContent(() => renderInfo(`No ${OA_DIR}/ directory found.`));
22963
23084
  }
22964
23085
  process.stdout.write("\x1Bc");
23086
+ },
23087
+ contextSave() {
23088
+ try {
23089
+ const entry = {
23090
+ savedAt: (/* @__PURE__ */ new Date()).toISOString(),
23091
+ task: lastSubmittedPrompt || "(manual save)",
23092
+ summary: `Manual context save. ${sessionToolCallCount} tool calls, ${sessionFilesTouched.length} files modified.`,
23093
+ filesModified: [...sessionFilesTouched],
23094
+ toolCalls: sessionToolCallCount,
23095
+ completed: false,
23096
+ model: currentConfig.model
23097
+ };
23098
+ saveSessionContext(repoRoot, entry);
23099
+ return true;
23100
+ } catch {
23101
+ return false;
23102
+ }
23103
+ },
23104
+ contextRestore() {
23105
+ return buildContextRestorePrompt(repoRoot);
23106
+ },
23107
+ setRestoredContext(ctx) {
23108
+ restoredSessionContext = ctx;
23109
+ },
23110
+ contextShow() {
23111
+ const ctx = loadSessionContext(repoRoot);
23112
+ if (!ctx || ctx.entries.length === 0) {
23113
+ return { entries: 0, lastSaved: null };
23114
+ }
23115
+ return { entries: ctx.entries.length, lastSaved: ctx.updatedAt };
22965
23116
  }
22966
23117
  };
22967
23118
  showPrompt();
@@ -22969,7 +23120,10 @@ async function startInteractive(config, repoPath) {
22969
23120
  const pendingTask = loadPendingTask(repoRoot);
22970
23121
  if (pendingTask) {
22971
23122
  setTimeout(() => {
23123
+ const sessionCtx = buildContextRestorePrompt(repoRoot);
22972
23124
  const resumeContext = [
23125
+ sessionCtx ? `[SESSION CONTEXT RESTORED]
23126
+ ${sessionCtx}` : "",
22973
23127
  `[RESUMED AFTER UPDATE] Original task: ${pendingTask.prompt}`,
22974
23128
  pendingTask.progressSummary ? `Progress so far: ${pendingTask.progressSummary}` : "",
22975
23129
  pendingTask.filesModified.length > 0 ? `Files modified before update: ${pendingTask.filesModified.join(", ")}` : "",
@@ -23191,9 +23345,18 @@ Summarize or analyze this transcription as appropriate.`;
23191
23345
  const displayText = isImage ? `[Image: ${cleanPath}]` : inputLineCount > 1 ? `[pasted ${inputLineCount} lines]` : fullInput;
23192
23346
  writeContent(() => renderUserMessage(displayText));
23193
23347
  lastSubmittedPrompt = fullInput;
23348
+ let taskInput = fullInput;
23349
+ if (restoredSessionContext) {
23350
+ taskInput = `${restoredSessionContext}
23351
+
23352
+ ---
23353
+
23354
+ NEW TASK: ${fullInput}`;
23355
+ restoredSessionContext = null;
23356
+ }
23194
23357
  try {
23195
23358
  statusBar.setProcessing(true);
23196
- const task = startTask(fullInput, currentConfig, repoRoot, voiceEngine, {
23359
+ const task = startTask(taskInput, currentConfig, repoRoot, voiceEngine, {
23197
23360
  enabled: streamEnabled,
23198
23361
  renderer: streamRenderer
23199
23362
  }, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.31.7",
3
+ "version": "0.31.9",
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",