open-agents-ai 0.187.15 → 0.187.16

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
@@ -13111,6 +13111,10 @@ var init_skill_builder = __esm({
13111
13111
  validate: {
13112
13112
  type: "boolean",
13113
13113
  description: "Whether to run the validation phase (default: true). Validation scores the generated skill and suggests improvements."
13114
+ },
13115
+ session_corrections: {
13116
+ type: "string",
13117
+ description: "Optional: corrections and failed approaches from the current session. Each correction becomes a hard rule in the generated SKILL.md. Format: one correction per line, e.g. 'FAILED: file_write too large \u2014 use file_edit instead'"
13114
13118
  }
13115
13119
  },
13116
13120
  required: ["skill_request"]
@@ -13134,9 +13138,10 @@ var init_skill_builder = __esm({
13134
13138
  durationMs: performance.now() - start2
13135
13139
  };
13136
13140
  }
13141
+ const sessionCorrections = String(args["session_corrections"] ?? "").trim();
13137
13142
  try {
13138
13143
  const seed = await this.analyzeSeed(skillRequest);
13139
- let skillContent = await this.expandSkill(seed);
13144
+ let skillContent = await this.expandSkill(seed, sessionCorrections);
13140
13145
  let validationInfo = "";
13141
13146
  if (shouldValidate) {
13142
13147
  const validation = await this.validateSkill(skillContent);
@@ -13205,15 +13210,21 @@ Validation: ${validation.pass ? "PASS" : "NEEDS IMPROVEMENT"} (${validation.over
13205
13210
  // -------------------------------------------------------------------------
13206
13211
  // Phase 2: Skill Expansion
13207
13212
  // -------------------------------------------------------------------------
13208
- async expandSkill(seed) {
13213
+ async expandSkill(seed, sessionCorrections) {
13209
13214
  const prompt = loadBuilderPrompt("skill-expansion.md", {
13210
13215
  seed_json: JSON.stringify(seed, null, 2)
13211
13216
  });
13217
+ const correctionBlock = sessionCorrections ? `
13218
+
13219
+ IMPORTANT \u2014 Session Corrections (encode as hard rules in the Rules section):
13220
+ These represent things that FAILED or were CORRECTED during the reference session.
13221
+ Each MUST appear as a rule in the generated SKILL.md:
13222
+ ${sessionCorrections}` : "";
13212
13223
  const response = await this.llmCall([
13213
13224
  { role: "system", content: prompt },
13214
13225
  {
13215
13226
  role: "user",
13216
- content: `Generate the complete SKILL.md for the "${seed.name}" skill.`
13227
+ content: `Generate the complete SKILL.md for the "${seed.name}" skill.${correctionBlock}`
13217
13228
  }
13218
13229
  ]);
13219
13230
  let content = response.trim();
@@ -260098,6 +260109,10 @@ var init_agenticRunner = __esm({
260098
260109
  // WO-E26: prevent double-emit
260099
260110
  _recentEnoents = [];
260100
260111
  // sliding window of last 8 tool calls
260112
+ _microcompactHintEmitted = false;
260113
+ // WO-LL-02: one memory hint per session
260114
+ _hookDenyHintCount = 0;
260115
+ // WO-LL-02: cap hint injection at 3 per session
260101
260116
  // -- Session Checkpointing (Priority 5) --
260102
260117
  _sessionId = `session-${Date.now()}`;
260103
260118
  _workingDirectory = "";
@@ -260326,6 +260341,10 @@ ${ctx3}`,
260326
260341
  content: `Microcompact: cleared ${cleared} old tool result(s), keeping ${keepResults} recent`,
260327
260342
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
260328
260343
  });
260344
+ if (!this._microcompactHintEmitted) {
260345
+ this._microcompactHintEmitted = true;
260346
+ this.pendingUserMessages.push(`[SYSTEM] Older tool results have been cleared to save context. If you discovered important patterns or facts, use memory_write to persist them before they are lost from context.`);
260347
+ }
260329
260348
  }
260330
260349
  }
260331
260350
  /** Register a tool for the agent to use */
@@ -260577,6 +260596,8 @@ TASK: ${task}` : task;
260577
260596
  let summary = "";
260578
260597
  let bruteForceCycle = 0;
260579
260598
  this._assistantTextEmitted = false;
260599
+ this._microcompactHintEmitted = false;
260600
+ this._hookDenyHintCount = 0;
260580
260601
  let pendingConstraintWarnings = [];
260581
260602
  let consecutiveTextOnly = 0;
260582
260603
  let loopInterventionCount = 0;
@@ -261094,6 +261115,10 @@ If you're stuck, try a completely different approach. Do NOT repeat what failed
261094
261115
  reason: hookCheck.reason ?? "hook",
261095
261116
  source: "hook"
261096
261117
  });
261118
+ if (this._hookDenyHintCount < 3) {
261119
+ this._hookDenyHintCount++;
261120
+ this.pendingUserMessages.push(`[SYSTEM] Tool "${tc.name}" was blocked: ${hookCheck.reason}. If this constraint should persist across sessions, use memory_write to save it (e.g., memory_write(topic="constraints", key="${tc.name}_blocked", value="${(hookCheck.reason ?? "hook").slice(0, 80)}")).`);
261121
+ }
261097
261122
  } else {
261098
261123
  const finalArgs = hookCheck.modifiedArgs ?? tc.arguments;
261099
261124
  try {
@@ -261189,6 +261214,10 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
261189
261214
  if (this._taskState.failedApproaches.length > 10) {
261190
261215
  this._taskState.failedApproaches.shift();
261191
261216
  }
261217
+ if (this._taskState.failedApproaches.length === 3) {
261218
+ this.pendingUserMessages.push(`[SYSTEM] You have ${this._taskState.failedApproaches.length} failed approaches this session. Consider using memory_write to save these failure patterns so you avoid them in future sessions:
261219
+ ` + this._taskState.failedApproaches.map((f2) => `- ${f2}`).join("\n"));
261220
+ }
261192
261221
  }
261193
261222
  }
261194
261223
  if (filePath && (tc.name === "file_read" || tc.name === "file_write" || tc.name === "file_edit" || tc.name === "batch_edit" || tc.name === "file_patch")) {
@@ -286218,11 +286247,39 @@ Clone a new voice: /voice clone <wav-file> [name]`);
286218
286247
  } else if (ctx3.isDreaming?.()) {
286219
286248
  renderWarning("Already dreaming. Use /dream stop to wake up first.");
286220
286249
  } else {
286221
- const mode = arg === "lucid" ? "lucid" : arg === "deep" ? "deep" : "default";
286250
+ const mode = arg === "lucid" ? "lucid" : arg === "deep" ? "deep" : arg === "consolidate" ? "consolidate" : "default";
286222
286251
  ctx3.dreamStart?.(mode);
286223
286252
  }
286224
286253
  return "handled";
286225
286254
  }
286255
+ case "skillify": {
286256
+ const runner = ctx3.getRunner?.();
286257
+ if (!runner) {
286258
+ renderWarning("No active task session. Run a task first, then /skillify.");
286259
+ return "handled";
286260
+ }
286261
+ const taskState = runner._taskState;
286262
+ const corrections = [];
286263
+ if (taskState?.failedApproaches?.length > 0) {
286264
+ for (const f2 of taskState.failedApproaches) {
286265
+ corrections.push(`FAILED: ${f2}`);
286266
+ }
286267
+ }
286268
+ if (taskState?.completedSteps?.length > 0) {
286269
+ for (const s2 of taskState.completedSteps.slice(-5)) {
286270
+ corrections.push(`SUCCEEDED: ${s2}`);
286271
+ }
286272
+ }
286273
+ const skillRequest = arg || taskState?.goal || "workflow from this session";
286274
+ const correctionStr = corrections.length > 0 ? corrections.join("\n") : "";
286275
+ renderInfo(`Extracting skill from session (${corrections.length} corrections/steps)...`);
286276
+ const skillifyPrompt = `Use the skill_build tool to create a skill from this session:
286277
+ skill_build(skill_request="${skillRequest}"` + (correctionStr ? `, session_corrections="${correctionStr.replace(/"/g, '\\"')}"` : "") + `)
286278
+
286279
+ The session corrections MUST become hard rules in the SKILL.md Rules section.`;
286280
+ ctx3.injectMessage?.(skillifyPrompt);
286281
+ return "handled";
286282
+ }
286226
286283
  case "cohere": {
286227
286284
  await showCohereDashboard(ctx3);
286228
286285
  return "handled";
@@ -293438,6 +293495,9 @@ var init_dream_engine = __esm({
293438
293495
  if (this.state.active) {
293439
293496
  throw new Error("Already dreaming. Use /dream stop first.");
293440
293497
  }
293498
+ if (mode === "consolidate") {
293499
+ return this.runConsolidation(onEvent);
293500
+ }
293441
293501
  this.abortController = new AbortController();
293442
293502
  const totalCycles = mode === "deep" ? 3 : mode === "lucid" ? 2 : 1;
293443
293503
  this.state = {
@@ -294259,6 +294319,99 @@ ${files.map((f2) => `- [\`${f2}\`](./${f2})`).join("\n")}
294259
294319
  } catch {
294260
294320
  }
294261
294321
  }
294322
+ /**
294323
+ * WO-LL-01: Memory Consolidation — runs the consolidation prompt through
294324
+ * AgenticRunner with read/write tools targeting .oa/memory/.
294325
+ *
294326
+ * 4-phase: Orient → Gather → Consolidate → Prune
294327
+ * Hannover reference: services/autoDream/autoDream.ts
294328
+ */
294329
+ async runConsolidation(onEvent) {
294330
+ this.state = {
294331
+ mode: "consolidate",
294332
+ active: true,
294333
+ cycleCount: 1,
294334
+ currentCycle: 1,
294335
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
294336
+ results: []
294337
+ };
294338
+ renderInfo("Memory consolidation starting \u2014 Phase 1: Orient \u2192 Phase 2: Gather \u2192 Phase 3: Consolidate \u2192 Phase 4: Prune");
294339
+ const memoryDir = join70(this.repoRoot, ".oa", "memory");
294340
+ mkdirSync25(memoryDir, { recursive: true });
294341
+ let prompt;
294342
+ try {
294343
+ prompt = loadPrompt3("tui/dream-consolidate.md", {
294344
+ memoryDir,
294345
+ lookbackEntries: "10"
294346
+ });
294347
+ } catch {
294348
+ prompt = `Consolidate the memory files in ${memoryDir}. List the directory, read files, merge duplicates, delete stale entries, update the index.`;
294349
+ }
294350
+ const backend = new OllamaAgenticBackend(this.config.backendUrl, this.config.model, this.config.apiKey);
294351
+ const modelTier = getModelTier(this.config.model);
294352
+ const runner = new AgenticRunner(backend, {
294353
+ maxTurns: 20,
294354
+ maxTokens: 4096,
294355
+ temperature: 0,
294356
+ requestTimeoutMs: this.config.timeoutMs,
294357
+ taskTimeoutMs: this.config.timeoutMs * 3,
294358
+ compactionThreshold: 24e3,
294359
+ contextWindowSize: 32768,
294360
+ modelTier
294361
+ });
294362
+ const tools = [
294363
+ new FileReadTool(this.repoRoot),
294364
+ new FileWriteTool(this.repoRoot),
294365
+ new FileEditTool(this.repoRoot),
294366
+ new ListDirectoryTool(this.repoRoot),
294367
+ new GrepSearchTool(this.repoRoot),
294368
+ new GlobFindTool(this.repoRoot),
294369
+ new ShellTool(this.repoRoot),
294370
+ new MemoryReadTool(this.repoRoot),
294371
+ new MemoryWriteTool(this.repoRoot),
294372
+ new MemorySearchTool(this.repoRoot)
294373
+ ].map((t2) => ({
294374
+ name: t2.name,
294375
+ description: t2.description,
294376
+ parameters: t2.parameters,
294377
+ execute: t2.execute.bind(t2)
294378
+ }));
294379
+ tools.push({
294380
+ name: "task_complete",
294381
+ description: "Signal consolidation is done. Args: {summary: string}",
294382
+ parameters: { type: "object", properties: { summary: { type: "string" } }, required: ["summary"] },
294383
+ execute: async (args) => ({ success: true, output: String(args["summary"] ?? "done") })
294384
+ });
294385
+ for (const t2 of tools)
294386
+ runner.registerTool(t2);
294387
+ if (onEvent)
294388
+ runner.onEvent(onEvent);
294389
+ const startMs = Date.now();
294390
+ try {
294391
+ const result = await runner.run(prompt);
294392
+ const durationMs = Date.now() - startMs;
294393
+ this.state.results.push({
294394
+ cycle: 1,
294395
+ stage: "consolidation",
294396
+ ideas: [],
294397
+ proposals: [],
294398
+ filesGenerated: [],
294399
+ durationMs
294400
+ });
294401
+ try {
294402
+ writeFileSync25(join70(memoryDir, ".last-consolidation"), JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString(), summary: result.summary?.slice(0, 500) }) + "\n");
294403
+ } catch {
294404
+ }
294405
+ renderInfo(`Consolidation complete: ${result.turns} turns, ${result.toolCalls} tool calls, ${(durationMs / 1e3).toFixed(1)}s`);
294406
+ if (result.summary)
294407
+ renderInfo(`Summary: ${result.summary.slice(0, 200)}`);
294408
+ } catch (err) {
294409
+ renderWarning(`Consolidation failed: ${err instanceof Error ? err.message : String(err)}`);
294410
+ }
294411
+ this.state.active = false;
294412
+ this.saveDreamState();
294413
+ return this.state;
294414
+ }
294262
294415
  /** Save dream state for resume/inspection */
294263
294416
  saveDreamState() {
294264
294417
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "open-agents-ai",
3
- "version": "0.187.15",
3
+ "version": "0.187.16",
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",
@@ -0,0 +1,48 @@
1
+ # Memory Consolidation
2
+
3
+ You are performing a memory consolidation pass — a reflective sweep over your memory files. Synthesize what you've learned recently into durable, well-organized memories so future sessions orient quickly.
4
+
5
+ Memory directory: `{{memoryDir}}`
6
+
7
+ ---
8
+
9
+ ## Phase 1: Orient
10
+
11
+ - Use list_directory to see what exists in the memory directory
12
+ - Read the memory index file (if it exists) to understand current organization
13
+ - Skim 2-3 existing memory files to understand the current structure and avoid duplicates
14
+
15
+ ## Phase 2: Gather Recent Signal
16
+
17
+ Look for information worth persisting or updating. Focus on:
18
+
19
+ 1. **Memory files with stale data** — facts that contradict what you see in the codebase now
20
+ 2. **Duplicate memories** — two files tracking the same topic (merge them)
21
+ 3. **Relative dates** — convert "yesterday", "last week" to absolute dates
22
+ 4. **Low-confidence entries** — memories that were speculative and can now be verified or removed
23
+
24
+ If session diary exists at `.oa/context/session-diary.md`, read the last {{lookbackEntries}} entries for recent session context.
25
+
26
+ ## Phase 3: Consolidate
27
+
28
+ For each thing worth updating:
29
+ - **MERGE** near-duplicate files into one (keep the better one, delete the other)
30
+ - **UPDATE** facts that have drifted from reality
31
+ - **CONVERT** relative dates to absolute (e.g., "yesterday" → the actual date)
32
+ - **DELETE** memories that are clearly wrong or superseded
33
+ - **PROMOTE** speculative entries that have been validated multiple times
34
+
35
+ Use file_edit for updates, file_write for new consolidated files, and shell to remove obsolete files.
36
+
37
+ ## Phase 4: Prune and Index
38
+
39
+ If a memory index file exists:
40
+ - Keep it under 200 lines
41
+ - Each entry: one line, under 150 chars
42
+ - Remove pointers to deleted/merged memories
43
+ - Add pointers to new or updated memories
44
+ - Resolve contradictions between index and actual files
45
+
46
+ ---
47
+
48
+ When done, call task_complete with a summary of what you consolidated, updated, merged, or pruned. If nothing changed (memories are already clean), say so.