openhermes 1.3.0 → 1.4.1

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/autorecall.mjs CHANGED
@@ -4,6 +4,8 @@ import fs from "node:fs"
4
4
  import { atomicWriteJson, fingerprintEnvironment, isTruthy, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
5
5
  import { getDataRoot, getCacheRoot, getMemoryRoot, getRecallRoot, getRuntimeRoot } from "./lib/paths.mjs"
6
6
 
7
+ const OLD_BASE = path.join(os.homedir(), ".config", "opencode", "openhermes")
8
+
7
9
  function readJson(fp, fallback) {
8
10
  try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
9
11
  }
@@ -75,24 +77,28 @@ function formatMemoryWriteGap(memory) {
75
77
  }
76
78
 
77
79
  async function loadMemoryAndWriteCache(projectKey, directory) {
78
- const OLD_MEMORY = path.join(os.homedir(), ".config", "opencode", "openhermes", "memory")
79
- const OLD_CACHE = path.join(os.homedir(), ".config", "opencode", "openhermes", "memory", "recall")
80
80
  const SENTINEL = path.join(getDataRoot(), ".migrated-from-v1")
81
81
  if (!fs.existsSync(SENTINEL)) {
82
- if (fs.existsSync(OLD_MEMORY)) {
83
- fs.cpSync(OLD_MEMORY, getMemoryRoot(), { recursive: true })
84
- fs.rmSync(OLD_MEMORY, { recursive: true, force: true })
82
+ const oldMemory = path.join(OLD_BASE, "memory")
83
+ if (fs.existsSync(oldMemory)) {
84
+ fs.cpSync(oldMemory, getMemoryRoot(), { recursive: true })
85
+ fs.rmSync(oldMemory, { recursive: true, force: true })
85
86
  }
86
- if (fs.existsSync(OLD_CACHE)) {
87
+ const oldCache = path.join(OLD_BASE, "memory", "recall")
88
+ if (fs.existsSync(oldCache)) {
87
89
  fs.mkdirSync(getRecallRoot(), { recursive: true })
88
- const files = fs.readdirSync(OLD_CACHE).filter(f => f.endsWith(".json"))
89
- for (const f of files) fs.cpSync(path.join(OLD_CACHE, f), path.join(getRecallRoot(), f))
90
+ const files = fs.readdirSync(oldCache).filter(f => f.endsWith(".json"))
91
+ for (const f of files) fs.cpSync(path.join(oldCache, f), path.join(getRecallRoot(), f))
90
92
  }
91
- const oldRuntime = path.join(os.homedir(), ".config", "opencode", "openhermes", "runtime")
93
+ const oldRuntime = path.join(OLD_BASE, "runtime")
92
94
  if (fs.existsSync(oldRuntime)) {
93
95
  fs.cpSync(oldRuntime, getRuntimeRoot(), { recursive: true })
94
96
  fs.rmSync(oldRuntime, { recursive: true, force: true })
95
97
  }
98
+ const oldArchive = path.join(OLD_BASE, "archive")
99
+ if (fs.existsSync(oldArchive)) {
100
+ fs.rmSync(oldArchive, { recursive: true, force: true })
101
+ }
96
102
  fs.writeFileSync(SENTINEL, new Date().toISOString(), "utf8")
97
103
  }
98
104
 
package/bootstrap.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  import path from "node:path"
2
2
  import fs from "node:fs"
3
- import os from "node:os"
4
3
  import { fileURLToPath } from "node:url"
5
4
 
6
5
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
@@ -9,8 +8,7 @@ const RULES_DIR = path.join(HARNESS_DIR, "rules")
9
8
  const SKILLS_DIR = path.join(HARNESS_DIR, "skills")
10
9
  const CONSTITUTION_FILE = path.join(HARNESS_DIR, "constitution", "soul.md")
11
10
  const RUNTIME_FILE = path.join(HARNESS_DIR, "instructions", "RUNTIME.md")
12
- const TOOLS_SOURCE_DIR = path.resolve(__dirname, "lib", "tools")
13
- const USER_TOOLS_DIR = path.join(os.homedir(), ".config", "opencode", "tools")
11
+
14
12
 
15
13
  let _bootstrapCache = undefined
16
14
 
@@ -116,35 +114,7 @@ function getOwnVersion() {
116
114
  } catch { return "1.0.0" }
117
115
  }
118
116
 
119
- function installToolFiles() {
120
- try {
121
- if (!fs.existsSync(TOOLS_SOURCE_DIR)) return
122
- const files = fs.readdirSync(TOOLS_SOURCE_DIR).filter(f => f.endsWith(".mjs") && f !== "_memory.mjs")
123
- if (!files.length) return
124
- fs.mkdirSync(USER_TOOLS_DIR, { recursive: true })
125
- const pkgVersion = getOwnVersion()
126
- const markerPath = path.join(USER_TOOLS_DIR, ".openhermes-version")
127
- let installedVersion = ""
128
- try { installedVersion = fs.readFileSync(markerPath, "utf8").trim() } catch {}
129
- if (installedVersion === pkgVersion) {
130
- const existing = fs.readdirSync(USER_TOOLS_DIR).filter(f => f.endsWith(".mjs"))
131
- const needed = [...files, "_memory.mjs"]
132
- if (needed.every(f => existing.includes(f))) return
133
- }
134
- for (const f of ["_memory.mjs", ...files]) {
135
- const src = path.join(TOOLS_SOURCE_DIR, f)
136
- const dst = path.join(USER_TOOLS_DIR, f)
137
- if (fs.existsSync(src)) fs.copyFileSync(src, dst)
138
- }
139
- fs.writeFileSync(markerPath, pkgVersion, "utf8")
140
- process.stderr.write(`[openhermes-bootstrap] installed ${files.length + 1} tool files (v${pkgVersion})\n`)
141
- } catch (err) {
142
- process.stderr.write(`[openhermes-bootstrap] tool install error: ${err.message}\n`)
143
- }
144
- }
145
-
146
117
  export const BootstrapPlugin = async ({ client, directory }) => {
147
- installToolFiles()
148
118
 
149
119
  const getContent = () => {
150
120
  if (_bootstrapCache !== undefined) return _bootstrapCache
@@ -220,6 +190,23 @@ export const BootstrapPlugin = async ({ client, directory }) => {
220
190
  if (!config.agent[name]) config.agent[name] = def
221
191
  }
222
192
 
193
+ if (!config.agent["OpenHermes"]) {
194
+ config.agent["OpenHermes"] = {
195
+ description: "Fully autonomous primary coding agent (all tools allowed)",
196
+ mode: "primary",
197
+ color: "#F59E0B",
198
+ permission: {
199
+ bash: { "*": "allow" },
200
+ edit: "allow",
201
+ read: "allow",
202
+ task: { "*": "allow" }
203
+ }
204
+ }
205
+ }
206
+ if (!config.default_agent) {
207
+ config.default_agent = "OpenHermes"
208
+ }
209
+
223
210
  config.command = config.command || {}
224
211
  const COMMANDS_DIR = path.join(HARNESS_DIR, "commands")
225
212
  const ct = (file) => `{file:${path.join(COMMANDS_DIR, file)}}\n\n$ARGUMENTS`
package/curator.mjs CHANGED
@@ -5,7 +5,7 @@ import { findUnsupportedSchemaKeywords, validateSchema } from "./lib/schema-vali
5
5
  import { atomicWriteJson, fingerprintEnvironment, fingerprintFile, isTruthy, redactSensitiveText, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
6
6
  import { fileURLToPath } from "node:url"
7
7
  import { dirname } from "node:path"
8
- import { getConfigRoot, getDataRoot, getMemoryRoot, getRuntimeRoot, getArchiveRoot, getSchemaRoot } from "./lib/paths.mjs"
8
+ import { getDataRoot, getMemoryRoot, getRuntimeRoot, getArchiveRoot } from "./lib/paths.mjs"
9
9
 
10
10
  const __dirname = dirname(fileURLToPath(import.meta.url))
11
11
 
@@ -78,10 +78,8 @@ function updateLoopState(root, patch) {
78
78
  }
79
79
 
80
80
  function loadSchema(classId) {
81
- const fp = path.join(getSchemaRoot(), `${classId}.schema.json`)
82
- try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch {}
83
- const bundled = path.join(__dirname, "schemas", `${classId}.schema.json`)
84
- try { return JSON.parse(fs.readFileSync(bundled, "utf8")) } catch { return null }
81
+ const fp = path.join(__dirname, "schemas", `${classId}.schema.json`)
82
+ try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return null }
85
83
  }
86
84
 
87
85
  function validateRecordAgainstSchema(record) {
@@ -377,7 +375,7 @@ async function handlePermissionReplied(directory, project, event) {
377
375
  environment_fingerprint: environmentFingerprint,
378
376
  }
379
377
  const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
380
- const auditSchema = loadSchema("audit") || readJson(path.join(__dirname, "schemas", "audit.schema.json"), null)
378
+ const auditSchema = loadSchema("audit")
381
379
  if (auditSchema) {
382
380
  const unsupported = findUnsupportedSchemaKeywords(auditSchema)
383
381
  if (!unsupported.length) {
@@ -398,18 +396,23 @@ async function handlePermissionReplied(directory, project, event) {
398
396
  export const CuratorPlugin = async ({ project, directory }) => {
399
397
  return {
400
398
  event: async ({ event }) => {
401
- const OLD_DATA = path.join(os.homedir(), ".config", "opencode", "openhermes", "memory")
399
+ const OLD_BASE = path.join(os.homedir(), ".config", "opencode", "openhermes")
402
400
  const SENTINEL = path.join(getDataRoot(), ".migrated-from-v1")
403
401
  if (!fs.existsSync(SENTINEL)) {
404
- if (fs.existsSync(OLD_DATA)) {
405
- fs.cpSync(OLD_DATA, getMemoryRoot(), { recursive: true })
406
- fs.rmSync(OLD_DATA, { recursive: true, force: true })
402
+ const oldMemory = path.join(OLD_BASE, "memory")
403
+ if (fs.existsSync(oldMemory)) {
404
+ fs.cpSync(oldMemory, getMemoryRoot(), { recursive: true })
405
+ fs.rmSync(oldMemory, { recursive: true, force: true })
407
406
  }
408
- const oldRuntime = path.join(os.homedir(), ".config", "opencode", "openhermes", "runtime")
407
+ const oldRuntime = path.join(OLD_BASE, "runtime")
409
408
  if (fs.existsSync(oldRuntime)) {
410
409
  fs.cpSync(oldRuntime, getRuntimeRoot(), { recursive: true })
411
410
  fs.rmSync(oldRuntime, { recursive: true, force: true })
412
411
  }
412
+ const oldArchive = path.join(OLD_BASE, "archive")
413
+ if (fs.existsSync(oldArchive)) {
414
+ fs.rmSync(oldArchive, { recursive: true, force: true })
415
+ }
413
416
  fs.writeFileSync(SENTINEL, new Date().toISOString(), "utf8")
414
417
  }
415
418
  if (event.type === "session.idle") {
@@ -6,10 +6,10 @@ import os from "os"
6
6
 
7
7
  import { atomicWriteJson, fingerprintEnvironment, sanitizeRecord, truncateText } from "./hardening.mjs"
8
8
  import { findUnsupportedSchemaKeywords, validateSchema } from "./schema-validator.mjs"
9
- import { getMemoryRoot, getConfigRoot, getDataRoot, getSchemaRoot, getRuntimeRoot } from "../lib/paths.mjs"
9
+ import { getMemoryRoot, getRuntimeRoot } from "../lib/paths.mjs"
10
10
 
11
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
12
- const PACKAGE_SCHEMAS = path.resolve(__dirname, "..", "schemas")
12
+ const SCHEMAS_DIR = path.resolve(__dirname, "..", "schemas")
13
13
  const MEMORY_DIR = getMemoryRoot()
14
14
 
15
15
  const CLASSES = ["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]
@@ -141,7 +141,7 @@ function handlePut(cls, id, dataStr) {
141
141
  const now = new Date().toISOString()
142
142
  const record = { ...parsed, id, class: cls, source: parsed.source ?? "agent", status: parsed.status ?? "active", created_at: parsed.created_at ?? now, updated_at: now }
143
143
 
144
- const schema = readJSON(path.join(getSchemaRoot(), `${cls}.schema.json`), null) || readJSON(path.join(PACKAGE_SCHEMAS, `${cls}.schema.json`), null)
144
+ const schema = readJSON(path.join(SCHEMAS_DIR, `${cls}.schema.json`), null)
145
145
  if (schema) {
146
146
  const unsupported = findUnsupportedSchemaKeywords(schema)
147
147
  if (unsupported.length) return `Unsupported schema keywords: ${unsupported.join(", ")}`
package/lib/paths.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import path from "node:path"
2
2
  import os from "node:os"
3
3
  import fs from "node:fs"
4
+ import { fileURLToPath } from "node:url"
4
5
 
5
6
  const HOME = process.env.USERPROFILE || os.homedir()
6
- const CONFIG_ROOT = path.join(HOME, ".config", "opencode", "openhermes")
7
7
  const DATA_ROOT = path.join(HOME, ".local", "share", "opencode", "openhermes")
8
8
  const CACHE_ROOT = path.join(HOME, ".cache", "opencode", "openhermes")
9
+ const PKG_DIR = path.dirname(fileURLToPath(import.meta.url))
9
10
 
10
11
  function resolveRoot(envVar, fallback) {
11
12
  if (!isTruthy(process.env.OPENCODE_ALLOW_PROJECT_HARNESS)) return fallback
@@ -16,7 +17,7 @@ function resolveRoot(envVar, fallback) {
16
17
  }
17
18
 
18
19
  export function getConfigRoot() {
19
- return resolveRoot("OPENCODE_ALLOW_PROJECT_HARNESS", CONFIG_ROOT)
20
+ return null // legacy — no longer used
20
21
  }
21
22
 
22
23
  export function getDataRoot() {
@@ -40,11 +41,11 @@ export function getRecallRoot() {
40
41
  }
41
42
 
42
43
  export function getArchiveRoot() {
43
- return path.join(getConfigRoot(), "archive")
44
+ return path.join(getDataRoot(), "archive")
44
45
  }
45
46
 
46
47
  export function getSchemaRoot() {
47
- return path.join(getConfigRoot(), "schemas")
48
+ return path.resolve(PKG_DIR, "..", "schemas")
48
49
  }
49
50
 
50
51
  function isTruthy(value) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openhermes",
3
- "version": "1.3.0",
3
+ "version": "1.4.1",
4
4
  "description": "OpenHermes plugin suite for OpenCode — autonomous checkpointing, native memory tools, subagent routing, slash commands, and skill-candidate detection.",
5
5
  "type": "module",
6
6
  "license": "MIT",