openhermes 1.2.2

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 (69) hide show
  1. package/README.md +281 -0
  2. package/autorecall.mjs +167 -0
  3. package/bootstrap.mjs +255 -0
  4. package/curator.mjs +470 -0
  5. package/harness/commands/build-fix.md +60 -0
  6. package/harness/commands/code-review.md +71 -0
  7. package/harness/commands/doctor.md +42 -0
  8. package/harness/commands/learn.md +37 -0
  9. package/harness/commands/memory-search.md +37 -0
  10. package/harness/commands/plan.md +53 -0
  11. package/harness/commands/security.md +93 -0
  12. package/harness/constitution/soul.md +76 -0
  13. package/harness/instructions/RUNTIME.md +21 -0
  14. package/harness/prompts/architect.txt +175 -0
  15. package/harness/prompts/build-error-resolver.md +37 -0
  16. package/harness/prompts/code-reviewer.md +33 -0
  17. package/harness/prompts/e2e-runner.txt +305 -0
  18. package/harness/prompts/explore.md +29 -0
  19. package/harness/prompts/planner.md +30 -0
  20. package/harness/prompts/security-reviewer.md +35 -0
  21. package/harness/rules/audit.md +84 -0
  22. package/harness/rules/checkpointing.md +75 -0
  23. package/harness/rules/context-loading.md +33 -0
  24. package/harness/rules/credential-exposure.md +0 -0
  25. package/harness/rules/delegation.md +76 -0
  26. package/harness/rules/memory-management.md +28 -0
  27. package/harness/rules/precedence.md +52 -0
  28. package/harness/rules/promotion.md +46 -0
  29. package/harness/rules/ranking.md +64 -0
  30. package/harness/rules/retrieval.md +94 -0
  31. package/harness/rules/runtime-guards.md +196 -0
  32. package/harness/rules/self-heal.md +79 -0
  33. package/harness/rules/session-start.md +34 -0
  34. package/harness/rules/skills-management.md +165 -0
  35. package/harness/rules/state-drift.md +192 -0
  36. package/harness/rules/verification.md +88 -0
  37. package/harness/skills/.bundled_manifest +17 -0
  38. package/harness/skills/.usage.json +6 -0
  39. package/harness/skills/api-design/SKILL.md +523 -0
  40. package/harness/skills/backend-patterns/SKILL.md +598 -0
  41. package/harness/skills/coding-standards/SKILL.md +549 -0
  42. package/harness/skills/e2e-testing/SKILL.md +326 -0
  43. package/harness/skills/frontend-patterns/SKILL.md +642 -0
  44. package/harness/skills/frontend-slides/SKILL.md +184 -0
  45. package/harness/skills/security-review/SKILL.md +495 -0
  46. package/harness/skills/strategic-compact/SKILL.md +131 -0
  47. package/harness/skills/tdd-workflow/SKILL.md +463 -0
  48. package/harness/skills/verification-loop/SKILL.md +126 -0
  49. package/index.mjs +5 -0
  50. package/lib/hardening.mjs +113 -0
  51. package/lib/memory-tools-plugin.mjs +265 -0
  52. package/lib/schema-validator.mjs +77 -0
  53. package/lib/tools/_memory.mjs +230 -0
  54. package/lib/tools/hm_get.mjs +13 -0
  55. package/lib/tools/hm_latest.mjs +12 -0
  56. package/lib/tools/hm_list.mjs +13 -0
  57. package/lib/tools/hm_put.mjs +14 -0
  58. package/lib/tools/hm_search.mjs +16 -0
  59. package/package.json +49 -0
  60. package/schemas/audit.schema.json +61 -0
  61. package/schemas/backlog.schema.json +42 -0
  62. package/schemas/checkpoint.schema.json +44 -0
  63. package/schemas/constraint.schema.json +41 -0
  64. package/schemas/decision.schema.json +42 -0
  65. package/schemas/instinct.schema.json +42 -0
  66. package/schemas/loop-state.schema.json +33 -0
  67. package/schemas/mistake.schema.json +43 -0
  68. package/schemas/verification_receipt.schema.json +67 -0
  69. package/skill-builder.mjs +113 -0
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ <p align="center">
2
+ <h1 align="center">&#9764; OpenHermes</h1>
3
+ <p align="center"><i>The Constitutional Router for <a href="https://opencode.ai">OpenCode</a></i></p>
4
+ </p>
5
+
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/openhermes"><img src="https://img.shields.io/npm/v/openhermes?style=for-the-badge&label=version&color=FFD700" alt="npm version"></a>
8
+ <a href="https://github.com/nathwn12/openhermes/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=for-the-badge" alt="License: MIT"></a>
9
+ <a href="https://opencode.ai"><img src="https://img.shields.io/badge/runs%20on-OpenCode-6366f1?style=for-the-badge" alt="Runs on OpenCode"></a>
10
+ <a href="https://github.com/nathwn12/openhermes/issues"><img src="https://img.shields.io/badge/issues-welcome-orange?style=for-the-badge" alt="Issues welcome"></a>
11
+ </p>
12
+
13
+ ---
14
+
15
+ **Your OpenCode agent, leveled up.** Add it to your plugins — your agent gains a personality, a memory, a conscience, 7 specialist subagents, 7 slash commands, 5 native memory tools, 10 procedural skills, and the discipline to self-improve.
16
+
17
+ ```bash
18
+ npm i openhermes
19
+ ```
20
+
21
+ ---
22
+
23
+ > &#9764; **Inspired by [Hermes Agent](https://github.com/NousResearch/hermes-agent)** — Nous Research's self-improving agent that brought closed learning loops, skill creation, and cross-session memory to the agent ecosystem. OpenHermes reimagines that vision **native to the OpenCode platform**: zero dependencies, no sidecars, no installers. Your entire agent OS in a single npm package.
24
+ >
25
+ > **Pruning inspiration**: The autonomous context-pressure system is heavily inspired by [Opencode-DCP (Dynamic Context Pruning)](https://github.com/Opencode-DCP/opencode-dynamic-context-pruning). The curator plugin's compaction-trigger logic and recall-cache freshness model are direct ports of DCP's approach, adapted to run inside OpenHermes with no sidecars or installers.
26
+
27
+ ---
28
+
29
+ ## What OpenHermes Does For Your Agent
30
+
31
+ <table>
32
+ <tr><td width="160"><b>&#129302; Personality Layer</b></td><td>A 11-principle constitution (soul.md) injected into every session — pragmatic, concise, subagent-first, verify-don't-claim. Your agent stops rambling and starts delivering.</td></tr>
33
+ <tr><td><b>&#128204; Delegation Discipline</b></td><td>Mandated routing table — every non-trivial task goes to the right specialist subagent. Main context stays clean, coordination-only. No more bloated chat logs.</td></tr>
34
+ <tr><td><b>&#128190; 9-Class Durable Memory</b></td><td>Checkpoints, decisions, constraints, instincts, mistakes, backlog items, audit reports, verification receipts, and session recall — all schema-validated, fingerprint-verified, persisted to disk.</td></tr>
35
+ <tr><td><b>&#129520; Precision-First Retrieval</b></td><td>Gated retrieval with anti-spam controls. <code>hm_latest</code> → <code>hm_search</code> → <code>hm_get</code> → <code>hm_list</code>. No full-index reads unless explicitly scoped. Memory stays lean.</td></tr>
36
+ <tr><td><b>&#128293; Autonomous Checkpointing</b></td><td>Pre-compaction snapshots capture mission, current state, next actions, active decisions, blockers, and risk notes — so compaction never loses the plot.</td></tr>
37
+ <tr><td><b>&#128260; Closed Learning Loop</b></td><td>Mistakes are logged with root cause + prevention rule. Complex sessions auto-generate skill-candidate backlogs. Strike tracking escalates repeat failures. The agent gets better — you don't have to teach it twice.</td></tr>
38
+ <tr><td><b>&#128736; 10 Bundled Procedural Skills</b></td><td>Pre-built skills for API design, backend patterns, coding standards, E2E testing, frontend patterns, security reviews, strategic compaction, TDD workflow, verification loops, and presentation building. Discovered automatically — use <code>skill</code> tool to list and load.</td></tr>
39
+ <tr><td><b>&#129513; Zero Infrastructure</b></td><td>No Python. No uv. No Docker. No PostgreSQL. No gateway. No cron daemon. Just Node.js — which OpenCode's Bun runtime already bundles. Everything runs inside your existing OpenCode session.</td></tr>
40
+ </table>
41
+
42
+ ---
43
+
44
+ ## Setup
45
+
46
+ Add one line to your `opencode.json`:
47
+
48
+ ```json
49
+ {
50
+ "plugin": ["openhermes"]
51
+ }
52
+ ```
53
+
54
+ That's it. **No other config needed.** The plugin auto-registers:
55
+
56
+ | What | Details |
57
+ |------|---------|
58
+ | **7 subagents** | `architect`, `planner`, `code-reviewer`, `security-reviewer`, `build-error-resolver`, `e2e-runner`, `explore` |
59
+ | **7 slash commands** | `/plan`, `/build-fix`, `/code-review`, `/security`, `/doctor`, `/memory-search`, `/learn` |
60
+ | **5 native memory tools** | `hm_put`, `hm_get`, `hm_list`, `hm_latest`, `hm_search` — in-process, no MCP server needed |
61
+ | **10 procedural skills** | API design, backend patterns, coding standards, E2E testing, frontend patterns, frontend slides, security review, strategic compaction, TDD workflow, verification loop |
62
+ | **5 lifecycle plugins** | bootstrap, curator, autorecall, skill-builder, memory-tools |
63
+
64
+ You only need to define primary agents (like `build` or `OpenHermes`) in `opencode.json` — subagents are injected automatically.
65
+
66
+ <details>
67
+ <summary><b>What happens on your next session</b></summary>
68
+
69
+ 1. **Config hook** — BootstrapPlugin registers auto-config: 7 subagents, 7 commands, 10 skill dirs.
70
+ 2. **Chat transform hook** — ~12KB of context injected into the first user message:
71
+ - &#9733; **Constitution** (soul.md) — 11 immutable principles
72
+ - &#9733; **Runtime** (RUNTIME.md) — gather → delegate → verify → compress
73
+ - &#9733; **Router** (AGENTS.md) — delegation table, memory policy, escalation, with absolute paths to every rule
74
+ 3. **Session created** — AutorecallPlugin builds recall cache from prior session memory
75
+ 4. **Tools execute** — SkillBuilderPlugin watches tool calls and subagent spawns; MemoryToolsPlugin provides 5 native tools immediately
76
+ 5. **Session idle** — CuratorPlugin snapshots checkpoint + verification receipt
77
+ 6. **Session error** — CuratorPlugin logs mistake with root cause + prevention rule
78
+ 7. **Compaction** — CuratorPlugin force-writes pre-compaction checkpoint, injects state into buffer
79
+
80
+ The LLM reads rules on demand via the injected paths. Memory directories auto-create. Everything Just Works™.
81
+
82
+ </details>
83
+
84
+ ---
85
+
86
+ ## The Five Plugins
87
+
88
+ | Plugin | Triggers On | What It Does |
89
+ |--------|------------|--------------|
90
+ | **BootstrapPlugin** | `config`, `chat.transform` | Registers 7 subagents, 7 commands, 10 skill paths. Injects constitution + router + runtime. |
91
+ | **MemoryToolsPlugin** | — | Registers 5 native tools: `hm_put`, `hm_get`, `hm_list`, `hm_latest`, `hm_search`. Runs in-process — no MCP server needed. |
92
+ | **CuratorPlugin** | `session.idle`, `.compacted`, `.error`, `.compacting`, `permission.replied` | Writes checkpoints, logs mistakes, records audits, injects state into compaction. |
93
+ | **AutorecallPlugin** | `session.created` | Loads memory from disk, builds session recall cache. |
94
+ | **SkillBuilderPlugin** | `session.idle`, `.created`, `tool.execute.after` | Detects complex sessions (8+ tool calls or 2+ subagent spawns) → creates skill-candidate backlogs. |
95
+
96
+ ---
97
+
98
+ ## Memory Architecture
99
+
100
+ Nine memory classes, all schema-validated before persistence, stored at `~/.config/opencode/openhermes/memory/`:
101
+
102
+ | Class | Format | Purpose |
103
+ |-------|--------|---------|
104
+ | `checkpoint` | JSON | Pre-compaction snapshots: mission, current state, next actions, blockers |
105
+ | `constraint` | JSON | Hard limits, env realities, safety rules — enforced by precedence engine |
106
+ | `decision` | JSON | Durable project choices — shapes all future behavior |
107
+ | `instinct` | JSON | Reusable trigger→action patterns with success/failure tracking |
108
+ | `backlog` | JSON | Evidence-backed self-improvement items with acceptance criteria |
109
+ | `mistake` | JSONL | Failure registry: type, root cause, fix, prevention rule, strike count |
110
+ | `audit` | JSON | Structured quality/integrity evaluations with health scores |
111
+ | `verification_receipt` | JSON | Cached verification results keyed by artifact fingerprint |
112
+ | `recall` | JSON | Session-start cache aggregating active state for compaction injection |
113
+
114
+ **Runtime hardening**: All records pass through `sanitizeRecord()` (strips `__proto__`, `constructor`, `prototype`), `redactSensitiveText()` (strips bearer tokens, API keys, passwords), and `truncateText()` before persistence. Schema validation gate runs before every write.
115
+
116
+ ---
117
+
118
+ ## Session Lifecycle
119
+
120
+ ```
121
+ session.startup (config hook)
122
+ │ BootstrapPlugin registers harness/skills/
123
+ │ OpenCode discovers 10 procedural skills
124
+
125
+ session.created (chat.transform + session.created hooks)
126
+ │ BootstrapPlugin injects constitution ▸ router ▸ runtime
127
+ │ AutorecallPlugin loads memory → writes recall cache
128
+ │ SkillBuilderPlugin resets session counters
129
+
130
+ tools execute...
131
+ │ SkillBuilderPlugin counts tool calls + subagent spawns
132
+
133
+ session.idle
134
+ │ CuratorPlugin snapshots checkpoint + verification receipt
135
+ │ SkillBuilderPlugin checks complexity → backlog entry if threshold met
136
+
137
+ session.compacted
138
+ │ CuratorPlugin updates loop-state → "compacted"
139
+
140
+ experimental.session.compacting
141
+ │ CuratorPlugin force-writes pre-compaction checkpoint
142
+ │ Injects harness state + recall context → compaction buffer
143
+
144
+ session.error
145
+ │ CuratorPlugin logs mistake (type, root cause, fix, prevention, strike)
146
+ │ Updates loop-state with error status
147
+
148
+ permission.replied
149
+ │ CuratorPlugin writes audit record for every permission decision
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Bundled Harness
155
+
156
+ The full OpenHermes framework ships inside the package — 45 files across 6 directories:
157
+
158
+ ```
159
+ harness/
160
+ ├── constitution/soul.md # 11 immutable personality principles
161
+ ├── instructions/RUNTIME.md # Session workflow: gather → delegate → verify
162
+ ├── rules/ (16 files)
163
+ │ ├── delegation.md # Mandatory subagent routing
164
+ │ ├── retrieval.md # Gated precision-first memory retrieval
165
+ │ ├── session-start.md # Session-start checklist and memory hydration
166
+ │ ├── credential-exposure.md # Secret redaction and credential exposure guard
167
+ │ ├── self-heal.md # T0→T3 escalation tiers
168
+ │ ├── verification.md # Skeptical evidence protocol
169
+ │ ├── memory-management.md # Dual-target memory + anti-spam
170
+ │ ├── precedence.md # 9-level conflict resolution
171
+ │ ├── checkpointing.md # Compaction snapshot discipline
172
+ │ ├── audit.md # Structured health checks
173
+ │ ├── skills-management.md # Progressive disclosure loading
174
+ │ ├── context-loading.md # Priority chain + size limits
175
+ │ ├── promotion.md # High-signal-only promotion
176
+ │ ├── ranking.md # Metadata-first retrieval
177
+ │ ├── runtime-guards.md # Stale assumption prevention
178
+ │ └── state-drift.md # Hash-based fingerprinting
179
+ ├── skills/ (10 directories)
180
+ │ ├── api-design/SKILL.md # REST API patterns (523 lines)
181
+ │ ├── backend-patterns/SKILL.md # Backend architecture (598 lines)
182
+ │ ├── coding-standards/SKILL.md # Baseline conventions (549 lines)
183
+ │ ├── e2e-testing/SKILL.md # Playwright E2E patterns (326 lines)
184
+ │ ├── frontend-patterns/SKILL.md # React/Next.js patterns (642 lines)
185
+ │ ├── frontend-slides/SKILL.md # HTML presentation builder (184 lines)
186
+ │ ├── security-review/SKILL.md # OWASP Top 10 checklist (495 lines)
187
+ │ ├── strategic-compact/SKILL.md # Context compaction strategy (131 lines)
188
+ │ ├── tdd-workflow/SKILL.md # Red-green-refactor discipline (463 lines)
189
+ │ └── verification-loop/SKILL.md # Pre-PR quality gates (126 lines)
190
+ ├── prompts/ (7 files)
191
+ │ # Subagent prompt templates: architect, build-error-resolver,
192
+ │ # code-reviewer, e2e-runner, explore, planner, security-reviewer
193
+ └── commands/ (8 files)
194
+ # Slash command templates: build-fix, code-review, doctor,
195
+ # learn, memory-search, plan, security
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Self-Healing Escalation
201
+
202
+ When your agent makes a mistake, OpenHermes doesn't just log it — it escalates:
203
+
204
+ | Tier | Trigger | Action |
205
+ |------|---------|--------|
206
+ | **T0** | Any mistake | Observe → log mistake record → smallest safe correction → verify |
207
+ | **T1** | Same mistake repeats within 7 days | Add prevention rule → targeted verification |
208
+ | **T2** | Prevention failed / systemic issue | Delegate to specialist → audit → backlog item |
209
+ | **T3** | Cascading failures | Constrained safe mode: narrow claims, preserve receipts, produce handoff |
210
+
211
+ No self-termination. No grandstanding. Narrow, log, recover, improve.
212
+
213
+ ---
214
+
215
+ ## Environment Variables
216
+
217
+ | Variable | Default | Effect |
218
+ |----------|---------|--------|
219
+ | `OPENCODE_ALLOW_PROJECT_HARNESS` | `false` | Use project-local harness at `.opencode/openhermes/` |
220
+ | `OPENCODE_CURATOR_LOGS` | `false` | Enable curator diagnostic output to stderr |
221
+
222
+ ---
223
+
224
+ ## Architecture
225
+
226
+ ```
227
+ openhermes/
228
+ ├── index.mjs # Re-exports all 5 plugins
229
+ ├── bootstrap.mjs # Config hook (agents/commands/skills) + chat.transform
230
+ ├── autorecall.mjs # Recall cache builder
231
+ ├── curator.mjs # Lifecycle hooks engine (~470 lines)
232
+ ├── skill-builder.mjs # Complexity detection engine
233
+ ├── lib/
234
+ │ ├── memory-tools-plugin.mjs # 5 native memory tools (hm_put/get/list/latest/search)
235
+ │ ├── hardening.mjs # atomicWriteJson, fingerprint, sanitize, redact
236
+ │ └── schema-validator.mjs # Draft-07 subset validator
237
+ ├── schemas/ # 9 JSON schemas for memory validation
238
+ ├── harness/ # Full framework (44 files)
239
+ └── package.json
240
+ ```
241
+
242
+ **Minimal npm dependency footprint.** Tool definitions use OpenCode's tiny `@opencode-ai/plugin` SDK. No postinstall scripts. No native compilation.
243
+
244
+ ---
245
+
246
+ ## Dependencies
247
+
248
+ - **Node.js >= 18** — `node:path`, `node:fs`, `node:os`, `node:url`, `node:crypto`
249
+ - **OpenCode** — provides the Bun runtime, plugin loader, hook dispatcher, and `skill` tool
250
+
251
+ ---
252
+
253
+ ## Why OpenHermes ≠ Hermes Agent
254
+
255
+ | | Hermes Agent | OpenHermes |
256
+ |--|------------|------------|
257
+ | **Platform** | Standalone agent with custom TUI, multi-platform gateway, cron scheduler | OpenCode-native plugin — runs *inside* your existing setup |
258
+ | **Installation** | `curl | bash` + Python 3.11 + uv + Docker (optional) + PostgreSQL (optional) | `npm i` — that's it |
259
+ | **Infrastructure** | Sidecar processes, gateway daemon, FTS5 database, Honcho user modeling | Zero sidecars — everything is a plugin hook |
260
+ | **Memory** | Honcho dialectic user profiles + skills system | Schema-validated 9-class memory + in-process native tools |
261
+ | **Skills** | Agentskills.io standard, self-created + improving | SKILL.md progressive disclosure, skill-builder auto-detection |
262
+ | **Context** | Context files + session search with LLM summarization | Harness injection at session start, recall cache at compaction |
263
+ | **Philosophy** | "The self-improving agent" — feature-rich, platform-expansive | "The constitutional router" — discipline-first, precision-only |
264
+
265
+ Both are &#9764; messengers. Different mediums.
266
+
267
+ ---
268
+
269
+ ## Contributing
270
+
271
+ Problems, ideas, improvements? [Open an issue](https://github.com/nathwn12/openhermes/issues). PRs welcome.
272
+
273
+ ---
274
+
275
+ ## License
276
+
277
+ MIT — see [LICENSE](LICENSE).
278
+
279
+ <p align="center">
280
+ <sub><b>&#9764;</b> Built with discipline. Inspired by <a href="https://github.com/NousResearch/hermes-agent">Hermes Agent</a>. Built for <a href="https://opencode.ai">OpenCode</a>.</sub>
281
+ </p>
package/autorecall.mjs ADDED
@@ -0,0 +1,167 @@
1
+ import path from "node:path"
2
+ import os from "node:os"
3
+ import fs from "node:fs"
4
+ import { atomicWriteJson, fingerprintEnvironment, isTruthy, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
5
+
6
+ function getHarnessRoot(directory) {
7
+ const home = process.env.USERPROFILE || os.homedir()
8
+ const configRoot = path.join(home, ".config", "opencode")
9
+ const projectHarness = path.join(directory, ".opencode", "openhermes")
10
+ const projectMemory = path.join(projectHarness, "memory")
11
+ if (isTruthy(process.env.OPENCODE_ALLOW_PROJECT_HARNESS)) {
12
+ try {
13
+ fs.accessSync(projectMemory)
14
+ return projectHarness
15
+ } catch {
16
+ }
17
+ }
18
+ return path.join(configRoot, "openhermes")
19
+ }
20
+
21
+ function readJson(fp, fallback) {
22
+ try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
23
+ }
24
+
25
+ function readJsonl(fp) {
26
+ try {
27
+ return fs.readFileSync(fp, "utf8").trim().split("\n").filter(Boolean).map(l => JSON.parse(l))
28
+ } catch { return [] }
29
+ }
30
+
31
+ function loadMemoryRecord(root, className, entry) {
32
+ const recordPath = path.join(root, "memory", className, `${entry.id}.json`)
33
+ const record = readJson(recordPath, null)
34
+ if (record && typeof record === "object") return record
35
+ return {
36
+ ...entry,
37
+ class: className,
38
+ scope: entry.scope || "harness",
39
+ }
40
+ }
41
+
42
+ function buildEnvironmentFingerprint(harnessRoot, directory, projectKey) {
43
+ return fingerprintEnvironment({
44
+ cwd: directory,
45
+ harnessRoot,
46
+ projectRoot: directory,
47
+ project: projectKey,
48
+ })
49
+ }
50
+
51
+ function formatContext(memory) {
52
+ const parts = []
53
+ if (memory.checkpoint) parts.push(`## Active Checkpoint\n${memory.checkpoint.summary || "N/A"}\n`)
54
+ if (memory.constraints.length) parts.push(`## Active Constraints\n${memory.constraints.map(c => `- ${c.summary}`).join("\n")}\n`)
55
+ if (memory.decisions.length) parts.push(`## Recent Decisions\n${memory.decisions.slice(0, 3).map(d => `- ${d.summary}`).join("\n")}\n`)
56
+ if (memory.mistakes.length) parts.push(`## Recent Mistakes (top ${Math.min(3, memory.mistakes.length)})\n${memory.mistakes.slice(0, 3).map(m => `- ${m.summary}`).join("\n")}\n`)
57
+ return parts.join("\n")
58
+ }
59
+
60
+ function validateMemoryRecord(record) {
61
+ const required = ["id", "class", "scope", "summary", "status"]
62
+ const missing = required.filter(r => !record[r])
63
+ if (missing.length) return false
64
+ if (record.confidence !== undefined && record.confidence < 0.3) return false
65
+ return true
66
+ }
67
+
68
+ function formatBacklogNudge(candidates) {
69
+ if (!candidates.length) return null
70
+ const count = candidates.length
71
+ const top = candidates.slice(0, 3).map((c, i) => `${i + 1}. ${c.summary || c.title || "unnamed candidate"}`).join("\n")
72
+ return [
73
+ `## Pending Skill Candidates (${count} open)`,
74
+ `The skill creation loop has ${count} unprocessed candidates.`,
75
+ `CRITICAL: Process the oldest candidate via /learn on this session start.`,
76
+ `Top candidates:`,
77
+ top,
78
+ `Trigger: /learn to create skills from these sessions.`,
79
+ `If none are skill-worthy, close them via hm_put with status:"closed".`
80
+ ].join("\n")
81
+ }
82
+
83
+ function formatMemoryWriteGap(memory) {
84
+ const gaps = []
85
+ if (memory.constraints.length === 0) gaps.push("constraints")
86
+ if (memory.decisions.length === 0) gaps.push("decisions")
87
+ if (!gaps.length) return null
88
+ return `## Memory Write Gap\nThese memory classes are empty: ${gaps.join(", ")}. Write at least one ${gaps[0]} this session.`
89
+ }
90
+
91
+ async function loadMemoryAndWriteCache(harnessRoot, projectKey, directory) {
92
+ const memory = { constraints: [], decisions: [], mistakes: [], checkpoint: null, pendingSkillCandidates: [] }
93
+ const fingerprint = buildEnvironmentFingerprint(harnessRoot, directory, projectKey)
94
+
95
+ const constraintsIndex = readJson(path.join(harnessRoot, "memory", "constraints", "index.json"), [])
96
+ if (Array.isArray(constraintsIndex)) memory.constraints = constraintsIndex.filter(e => e.status === "active")
97
+
98
+ const decisionsIndex = readJson(path.join(harnessRoot, "memory", "decisions", "index.json"), [])
99
+ if (Array.isArray(decisionsIndex)) {
100
+ memory.decisions = decisionsIndex
101
+ .filter(e => e.status === "active")
102
+ .map(entry => loadMemoryRecord(harnessRoot, "decisions", entry))
103
+ .filter(validateMemoryRecord)
104
+ }
105
+
106
+ const allMistakes = readJsonl(path.join(harnessRoot, "memory", "mistakes", "mistakes.jsonl"))
107
+ if (allMistakes.length) memory.mistakes = allMistakes.filter(e => e.status === "active").slice(0, 5)
108
+
109
+ const checkpointIndex = readJson(path.join(harnessRoot, "memory", "checkpoints", "index.json"), [])
110
+ if (Array.isArray(checkpointIndex) && checkpointIndex.length > 0) {
111
+ const latest = checkpointIndex.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))[0]
112
+ memory.checkpoint = readJson(path.join(harnessRoot, "memory", "checkpoints", `${latest.id}.json`), null)
113
+ }
114
+
115
+ const backlogIndex = readJson(path.join(harnessRoot, "memory", "backlog", "index.json"), [])
116
+ if (Array.isArray(backlogIndex)) {
117
+ memory.pendingSkillCandidates = backlogIndex.filter(e =>
118
+ e.status === "open" && (e.summary || "").includes("skill-candidate")
119
+ )
120
+ }
121
+
122
+ const contextParts = []
123
+ const baseContext = formatContext(memory)
124
+ if (baseContext) contextParts.push(baseContext)
125
+ const backlogNudge = formatBacklogNudge(memory.pendingSkillCandidates)
126
+ if (backlogNudge) contextParts.push(backlogNudge)
127
+ const writeGap = formatMemoryWriteGap(memory)
128
+ if (writeGap) contextParts.push(writeGap)
129
+
130
+ const context = contextParts.join("\n\n")
131
+ const boundedContext = context ? truncateText(context, 12000) : null
132
+
133
+ const cacheDir = path.join(harnessRoot, "memory", "recall")
134
+ fs.mkdirSync(cacheDir, { recursive: true })
135
+ atomicWriteJson(path.join(cacheDir, "cache.json"), sanitizeRecord({
136
+ context: boundedContext,
137
+ project: projectKey,
138
+ trust_mode: isTruthy(process.env.OPENCODE_ALLOW_PROJECT_HARNESS) ? "project" : "global",
139
+ harness_root: harnessRoot,
140
+ project_root: directory,
141
+ updated_at: new Date().toISOString(),
142
+ fingerprint,
143
+ freshness_marker: {
144
+ updated_at: new Date().toISOString(),
145
+ ttl_ms: 1800000,
146
+ },
147
+ stats: {
148
+ constraints: memory.constraints.length,
149
+ decisions: memory.decisions.length,
150
+ mistakes: memory.mistakes.length,
151
+ has_checkpoint: !!memory.checkpoint,
152
+ pending_skill_candidates: memory.pendingSkillCandidates.length
153
+ }
154
+ }, { maxStringLength: 4000 }))
155
+ }
156
+
157
+ export const AutorecallPlugin = async ({ project, directory }) => {
158
+ return {
159
+ event: async ({ event }) => {
160
+ if (event.type === "session.created") {
161
+ const harnessRoot = getHarnessRoot(directory)
162
+ const projectKey = project?.name || path.basename(directory)
163
+ await loadMemoryAndWriteCache(harnessRoot, projectKey, directory)
164
+ }
165
+ },
166
+ }
167
+ }