openhermes 2.8.0 → 4.0.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/CONTEXT.md +18 -0
- package/ETHOS.md +15 -0
- package/README.md +135 -292
- package/bootstrap.mjs +174 -512
- package/harness/agents/openhermes.md +87 -0
- package/harness/codex/CONSTITUTION.md +70 -148
- package/harness/codex/ROUTING.md +126 -0
- package/harness/commands/oh-doctor.md +26 -0
- package/harness/instructions/CONVENTIONS.md +206 -206
- package/harness/instructions/RUNTIME.md +54 -31
- package/harness/skills/oh-builder/SKILL.md +98 -0
- package/harness/skills/oh-caveman/SKILL.md +33 -0
- package/harness/skills/oh-expert/SKILL.md +121 -0
- package/harness/skills/oh-freeze/SKILL.md +28 -0
- package/harness/skills/oh-gauntlet/SKILL.md +119 -0
- package/harness/skills/oh-grill/SKILL.md +77 -0
- package/harness/skills/oh-guard/SKILL.md +33 -0
- package/harness/skills/oh-handoff/SKILL.md +33 -0
- package/harness/skills/oh-health/SKILL.md +90 -0
- package/harness/skills/oh-init/SKILL.md +78 -0
- package/harness/skills/oh-investigate/SKILL.md +35 -0
- package/harness/skills/oh-issue/SKILL.md +36 -0
- package/harness/skills/oh-learn/SKILL.md +28 -0
- package/harness/skills/oh-manifest/SKILL.md +84 -0
- package/harness/skills/oh-plan-review/SKILL.md +128 -0
- package/harness/skills/oh-planner/SKILL.md +159 -0
- package/harness/skills/oh-prd/SKILL.md +35 -0
- package/harness/skills/oh-retro/SKILL.md +33 -0
- package/harness/skills/oh-review/SKILL.md +110 -0
- package/harness/skills/oh-security/SKILL.md +110 -0
- package/harness/skills/oh-ship/SKILL.md +39 -0
- package/harness/skills/oh-skill-craft/SKILL.md +107 -0
- package/harness/skills/oh-skills-link/SKILL.md +29 -0
- package/harness/skills/oh-skills-list/SKILL.md +31 -0
- package/harness/skills/oh-triage/SKILL.md +36 -0
- package/index.mjs +3 -60
- package/lib/harness-resolver.mjs +77 -0
- package/lib/logger.mjs +62 -0
- package/package.json +49 -53
- package/test/plugins-behavioral.test.mjs +64 -0
- package/test/plugins.test.mjs +62 -0
- package/autorecall.mjs +0 -237
- package/curator.mjs +0 -482
- package/harness/commands/build-fix.md +0 -60
- package/harness/commands/checkpoint.md +0 -68
- package/harness/commands/code-review.md +0 -71
- package/harness/commands/doctor.md +0 -42
- package/harness/commands/eval.md +0 -89
- package/harness/commands/go-build.md +0 -87
- package/harness/commands/go-review.md +0 -71
- package/harness/commands/harness-audit.md +0 -90
- package/harness/commands/learn.md +0 -37
- package/harness/commands/loop-start.md +0 -38
- package/harness/commands/loop-status.md +0 -30
- package/harness/commands/memory-search.md +0 -37
- package/harness/commands/model-route.md +0 -32
- package/harness/commands/ohc.md +0 -13
- package/harness/commands/orchestrate.md +0 -88
- package/harness/commands/plan.md +0 -53
- package/harness/commands/quality-gate.md +0 -35
- package/harness/commands/refactor-clean.md +0 -102
- package/harness/commands/rust-build.md +0 -78
- package/harness/commands/rust-review.md +0 -65
- package/harness/commands/security.md +0 -93
- package/harness/commands/setup-pm.md +0 -65
- package/harness/commands/skill-create.md +0 -99
- package/harness/commands/test-coverage.md +0 -80
- package/harness/commands/update-codemaps.md +0 -81
- package/harness/commands/update-docs.md +0 -67
- package/harness/commands/verify.md +0 -68
- package/harness/prompts/architect.txt +0 -189
- package/harness/prompts/build-cpp.md +0 -98
- package/harness/prompts/build-error-resolver.md +0 -44
- package/harness/prompts/build-go.md +0 -340
- package/harness/prompts/build-java.md +0 -140
- package/harness/prompts/build-kotlin.md +0 -137
- package/harness/prompts/build-rust.md +0 -108
- package/harness/prompts/code-reviewer.md +0 -40
- package/harness/prompts/doc-updater.md +0 -206
- package/harness/prompts/docs-lookup.md +0 -71
- package/harness/prompts/e2e-runner.txt +0 -317
- package/harness/prompts/explore.md +0 -42
- package/harness/prompts/harness-optimizer.md +0 -42
- package/harness/prompts/loop-operator.md +0 -53
- package/harness/prompts/planner.md +0 -37
- package/harness/prompts/refactor-cleaner.md +0 -256
- package/harness/prompts/review-cpp.md +0 -81
- package/harness/prompts/review-database.md +0 -261
- package/harness/prompts/review-go.md +0 -257
- package/harness/prompts/review-java.md +0 -113
- package/harness/prompts/review-kotlin.md +0 -143
- package/harness/prompts/review-python.md +0 -101
- package/harness/prompts/review-rust.md +0 -77
- package/harness/prompts/security-reviewer.md +0 -42
- package/harness/prompts/tdd-guide.md +0 -228
- package/harness/rules/audit.md +0 -84
- package/harness/rules/checkpointing.md +0 -75
- package/harness/rules/context-loading.md +0 -33
- package/harness/rules/credential-exposure.md +0 -0
- package/harness/rules/delegation.md +0 -80
- package/harness/rules/handoff.md +0 -267
- package/harness/rules/memory-management.md +0 -28
- package/harness/rules/precedence.md +0 -52
- package/harness/rules/promotion.md +0 -46
- package/harness/rules/ranking.md +0 -64
- package/harness/rules/retrieval.md +0 -94
- package/harness/rules/runtime-guards.md +0 -196
- package/harness/rules/self-heal.md +0 -79
- package/harness/rules/session-start.md +0 -34
- package/harness/rules/skills-management.md +0 -165
- package/harness/rules/state-drift.md +0 -192
- package/harness/rules/verification.md +0 -88
- package/harness/scripts/sync-commands.mjs +0 -259
- package/harness/skills/.bundled_manifest +0 -17
- package/harness/skills/.usage.json +0 -6
- package/harness/skills/api-design/SKILL.md +0 -523
- package/harness/skills/backend-patterns/SKILL.md +0 -598
- package/harness/skills/coding-standards/SKILL.md +0 -549
- package/harness/skills/e2e-testing/SKILL.md +0 -326
- package/harness/skills/frontend-patterns/SKILL.md +0 -642
- package/harness/skills/frontend-slides/SKILL.md +0 -184
- package/harness/skills/security-review/SKILL.md +0 -495
- package/harness/skills/strategic-compact/SKILL.md +0 -131
- package/harness/skills/tdd-workflow/SKILL.md +0 -463
- package/harness/skills/verification-loop/SKILL.md +0 -126
- package/lib/ambient-memory.mjs +0 -167
- package/lib/handoff.mjs +0 -171
- package/lib/hardening.mjs +0 -146
- package/lib/memory-tools-plugin.mjs +0 -368
- package/lib/ohc/block-sync.mjs +0 -69
- package/lib/ohc/compress/search.mjs +0 -152
- package/lib/ohc/compress/state.mjs +0 -76
- package/lib/ohc/config.mjs +0 -185
- package/lib/ohc/message-ids.mjs +0 -178
- package/lib/ohc/notify.mjs +0 -135
- package/lib/ohc/protected-patterns.mjs +0 -55
- package/lib/ohc/prune-apply.mjs +0 -134
- package/lib/ohc/pruner.mjs +0 -608
- package/lib/ohc/reaper.mjs +0 -70
- package/lib/ohc/state.mjs +0 -265
- package/lib/ohc/strategies/deduplication.mjs +0 -72
- package/lib/ohc/strategies/index.mjs +0 -2
- package/lib/ohc/strategies/purge-errors.mjs +0 -43
- package/lib/ohc/token-utils.mjs +0 -26
- package/lib/ohc/updater.mjs +0 -132
- package/lib/paths.mjs +0 -49
- package/lib/schema-validator.mjs +0 -79
- package/lib/search.mjs +0 -48
- package/schemas/audit.schema.json +0 -82
- package/schemas/backlog.schema.json +0 -63
- package/schemas/checkpoint.schema.json +0 -65
- package/schemas/constraint.schema.json +0 -62
- package/schemas/decision.schema.json +0 -63
- package/schemas/instinct.schema.json +0 -63
- package/schemas/loop-state.schema.json +0 -33
- package/schemas/mistake.schema.json +0 -64
- package/schemas/verification_receipt.schema.json +0 -88
- package/skill-builder.mjs +0 -88
package/curator.mjs
DELETED
|
@@ -1,482 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
import fs from "node:fs"
|
|
3
|
-
import { findUnsupportedSchemaKeywords, validateSchema } from "./lib/schema-validator.mjs"
|
|
4
|
-
import { atomicWriteJson, buildEnvironmentFingerprint, fingerprintFile, readJson, redactSensitiveText, sanitizeRecord, truncateText } from "./lib/hardening.mjs"
|
|
5
|
-
import { fileURLToPath } from "node:url"
|
|
6
|
-
import { dirname } from "node:path"
|
|
7
|
-
import { getDataRoot, getMemoryRoot } from "./lib/paths.mjs"
|
|
8
|
-
|
|
9
|
-
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
|
-
|
|
11
|
-
const CHECKPOINT_DEBOUNCE_MS = 300000
|
|
12
|
-
const COMPACTION_CONTEXT_LIMIT = 12000
|
|
13
|
-
const lastCheckpoint = { ts: 0 }
|
|
14
|
-
const writtenThisSession = []
|
|
15
|
-
const CURATOR_LOGS = /^(1|true|yes)$/i.test(process.env.OPENCODE_CURATOR_LOGS || "")
|
|
16
|
-
|
|
17
|
-
const MEMORY_CLASSES = ["checkpoints", "mistakes", "audits", "verification_receipts", "constraints", "decisions", "instincts", "backlog"]
|
|
18
|
-
|
|
19
|
-
function ensureConsistency(root) {
|
|
20
|
-
const memoryRoot = path.join(root, "memory")
|
|
21
|
-
const runtimeRoot = path.join(root, "runtime")
|
|
22
|
-
|
|
23
|
-
for (const cls of MEMORY_CLASSES) {
|
|
24
|
-
const dir = path.join(memoryRoot, cls)
|
|
25
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
26
|
-
const indexPath = path.join(dir, "index.json")
|
|
27
|
-
if (!fs.existsSync(indexPath)) {
|
|
28
|
-
atomicWriteJson(indexPath, [])
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
fs.mkdirSync(runtimeRoot, { recursive: true })
|
|
33
|
-
const loopStatePath = path.join(runtimeRoot, "loop-state.json")
|
|
34
|
-
if (!fs.existsSync(loopStatePath)) {
|
|
35
|
-
atomicWriteJson(loopStatePath, { status: "idle", phase: "session.created" })
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function curatorLog(message) {
|
|
40
|
-
if (!CURATOR_LOGS) return
|
|
41
|
-
process.stderr.write(`${message}\n`)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function isMeaningfulText(value) {
|
|
45
|
-
return typeof value === "string" && value.trim().length > 0 && !/^(n\/a|none|tbd|placeholder|pending|session in progress|no active checkpoint content)$/i.test(value.trim())
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function safeLogMessage(message, limit = 160) {
|
|
49
|
-
return truncateText(redactSensitiveText(message || ""), limit)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function indexEntry(root, plural, record) {
|
|
53
|
-
const indexPath = path.join(root, "memory", plural, "index.json")
|
|
54
|
-
let index = readJson(indexPath, [])
|
|
55
|
-
if (!Array.isArray(index)) index = []
|
|
56
|
-
const entry = {
|
|
57
|
-
id: record.id,
|
|
58
|
-
summary: record.summary,
|
|
59
|
-
status: record.status,
|
|
60
|
-
updated_at: record.updated_at || record.created_at,
|
|
61
|
-
path: `openhermes/memory/${plural}/${record.id}.json`
|
|
62
|
-
}
|
|
63
|
-
const existing = index.findIndex(e => e.id === record.id)
|
|
64
|
-
if (existing >= 0) index[existing] = entry
|
|
65
|
-
else index.push(entry)
|
|
66
|
-
atomicWriteJson(indexPath, index)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function updateLoopState(root, patch) {
|
|
70
|
-
const statePath = path.join(root, "runtime", "loop-state.json")
|
|
71
|
-
const state = readJson(statePath, null) || {}
|
|
72
|
-
const next = {
|
|
73
|
-
...state,
|
|
74
|
-
...patch,
|
|
75
|
-
updated_at: patch.updated_at || new Date().toISOString(),
|
|
76
|
-
}
|
|
77
|
-
const schema = loadSchema("loop-state")
|
|
78
|
-
if (!schema) return false
|
|
79
|
-
const unsupported = findUnsupportedSchemaKeywords(schema)
|
|
80
|
-
if (unsupported.length) return false
|
|
81
|
-
const errors = validateSchema(schema, next, "$")
|
|
82
|
-
if (errors.length) return false
|
|
83
|
-
atomicWriteJson(statePath, next)
|
|
84
|
-
return true
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function loadSchema(classId) {
|
|
88
|
-
const fp = path.join(__dirname, "schemas", `${classId}.schema.json`)
|
|
89
|
-
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return null }
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function validateRecordAgainstSchema(record) {
|
|
93
|
-
const schema = loadSchema(record.class)
|
|
94
|
-
if (!schema) {
|
|
95
|
-
curatorLog(`[curator] validation fallback: no schema for "${record.class}"`)
|
|
96
|
-
const required = record.class === "checkpoint"
|
|
97
|
-
? ["id", "class", "summary", "mission", "current_state", "next_actions", "blockers", "risk_notes", "provenance", "created_at", "status"]
|
|
98
|
-
: ["id", "class", "summary", "provenance", "created_at", "status"]
|
|
99
|
-
const missing = required.filter(r => !record[r] && record[r] !== null)
|
|
100
|
-
if (missing.length) {
|
|
101
|
-
curatorLog(`[curator] validation failed: missing ${missing.join(", ")}`)
|
|
102
|
-
return false
|
|
103
|
-
}
|
|
104
|
-
if (record.class === "checkpoint" && record.provenance && !record.provenance.session_id) {
|
|
105
|
-
curatorLog(`[curator] validation failed: missing session_id`)
|
|
106
|
-
return false
|
|
107
|
-
}
|
|
108
|
-
return true
|
|
109
|
-
}
|
|
110
|
-
const unsupported = findUnsupportedSchemaKeywords(schema)
|
|
111
|
-
if (unsupported.length) {
|
|
112
|
-
curatorLog(`[curator] validation failed: unsupported fields ${unsupported.join(", ")}`)
|
|
113
|
-
return false
|
|
114
|
-
}
|
|
115
|
-
const errors = validateSchema(schema, record, "$")
|
|
116
|
-
if (errors.length) {
|
|
117
|
-
curatorLog(`[curator] validation failed: ${errors.join("; ")}`)
|
|
118
|
-
return false
|
|
119
|
-
}
|
|
120
|
-
if (record.class === "checkpoint") {
|
|
121
|
-
const requiredText = [record.mission, record.current_state]
|
|
122
|
-
if (!requiredText.every(isMeaningfulText)) return false
|
|
123
|
-
if (!Array.isArray(record.next_actions) || record.next_actions.length === 0) return false
|
|
124
|
-
if (!Array.isArray(record.blockers) || record.blockers.length === 0) return false
|
|
125
|
-
if (!Array.isArray(record.risk_notes) || record.risk_notes.length === 0) return false
|
|
126
|
-
}
|
|
127
|
-
return true
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function writeCheckpoint(root, project, directory, trigger, summary, options = {}) {
|
|
131
|
-
const now = Date.now()
|
|
132
|
-
if (!options.force && now - lastCheckpoint.ts < CHECKPOINT_DEBOUNCE_MS) return null
|
|
133
|
-
lastCheckpoint.ts = now
|
|
134
|
-
|
|
135
|
-
const ts = new Date().toISOString()
|
|
136
|
-
const id = `chk_${ts.replace(/[:.]/g, "-")}`
|
|
137
|
-
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
138
|
-
const isCompaction = trigger === "experimental.session.compacting"
|
|
139
|
-
const record = {
|
|
140
|
-
id,
|
|
141
|
-
class: "checkpoint",
|
|
142
|
-
scope: "session",
|
|
143
|
-
summary: summary || (isCompaction ? `Pre-compaction checkpoint for ${project?.name || path.basename(directory)}` : `Idle checkpoint for ${project?.name || path.basename(directory)}`),
|
|
144
|
-
mission: isCompaction
|
|
145
|
-
? "Preserve the current OpenHermes runtime state before compaction trims working context."
|
|
146
|
-
: "Preserve the current OpenHermes session state so the next turn can resume safely.",
|
|
147
|
-
current_state: isCompaction
|
|
148
|
-
? "A compaction run is starting; runtime memory, recall freshness, and loop-state validation are being hardened before context is reduced."
|
|
149
|
-
: "The session is idle and the curator is maintaining durable state, schema validation, and memory hygiene for the next turn.",
|
|
150
|
-
next_actions: isCompaction
|
|
151
|
-
? [
|
|
152
|
-
"Verify the pre-compaction checkpoint was written and indexed.",
|
|
153
|
-
"Inject only fresh recall context into the compaction buffer.",
|
|
154
|
-
"Confirm loop-state writes remain schema-valid after compaction.",
|
|
155
|
-
]
|
|
156
|
-
: [
|
|
157
|
-
"Keep loop-state and memory writes atomic.",
|
|
158
|
-
"Redact sensitive text before persisting new records.",
|
|
159
|
-
"Recheck stale recall cache handling on the next compaction pass.",
|
|
160
|
-
],
|
|
161
|
-
blockers: [
|
|
162
|
-
"No blocking runtime failure is present; this checkpoint captures a safe handoff state.",
|
|
163
|
-
],
|
|
164
|
-
risk_notes: isCompaction
|
|
165
|
-
? [
|
|
166
|
-
"A stale recall cache can reintroduce outdated compaction context if fingerprints drift.",
|
|
167
|
-
"Loop-state writes must continue to satisfy the required status, phase, heartbeat_at, and updated_at fields.",
|
|
168
|
-
]
|
|
169
|
-
: [
|
|
170
|
-
"Placeholder checkpoint content is no longer acceptable for durable handoff.",
|
|
171
|
-
"Sensitive text must be redacted before memory persistence.",
|
|
172
|
-
],
|
|
173
|
-
provenance: {
|
|
174
|
-
session_id: project?.session_id || `session-${Date.now()}`,
|
|
175
|
-
harness_root: root,
|
|
176
|
-
project_root: directory,
|
|
177
|
-
},
|
|
178
|
-
description: `Auto-curated checkpoint from curator plugin on ${trigger}`,
|
|
179
|
-
source: "agent",
|
|
180
|
-
status: "active",
|
|
181
|
-
created_at: ts,
|
|
182
|
-
updated_at: ts,
|
|
183
|
-
project: project?.name || path.basename(directory),
|
|
184
|
-
environment_fingerprint: environmentFingerprint,
|
|
185
|
-
}
|
|
186
|
-
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
187
|
-
if (!validateRecordAgainstSchema(safeRecord)) return null
|
|
188
|
-
const dir = path.join(root, "memory", "checkpoints")
|
|
189
|
-
atomicWriteJson(path.join(dir, `${id}.json`), safeRecord)
|
|
190
|
-
indexEntry(root, "checkpoints", safeRecord)
|
|
191
|
-
updateLoopState(root, {
|
|
192
|
-
last_checkpoint_id: id,
|
|
193
|
-
phase: trigger,
|
|
194
|
-
heartbeat_at: ts,
|
|
195
|
-
updated_at: ts,
|
|
196
|
-
status: trigger === "experimental.session.compacting" ? "active" : "idle",
|
|
197
|
-
})
|
|
198
|
-
if (writtenThisSession.length >= 100) writtenThisSession.shift()
|
|
199
|
-
writtenThisSession.push(id)
|
|
200
|
-
curatorLog(`[curator] checkpoint written: ${id} (trigger: ${trigger})`)
|
|
201
|
-
return id
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function writeMistakeRecord(root, project, directory, error) {
|
|
205
|
-
const ts = new Date().toISOString()
|
|
206
|
-
const errorMsg = typeof error === "object" && error !== null
|
|
207
|
-
? (error.message || JSON.stringify(error).slice(0, 200))
|
|
208
|
-
: (error?.toString() || "Unknown error")
|
|
209
|
-
const safeErrorMsg = truncateText(redactSensitiveText(errorMsg), 200)
|
|
210
|
-
const id = `mist_${ts.replace(/[:.]/g, "-")}`
|
|
211
|
-
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
212
|
-
const record = {
|
|
213
|
-
id,
|
|
214
|
-
class: "mistake",
|
|
215
|
-
scope: "harness",
|
|
216
|
-
summary: `Session error: ${safeErrorMsg}`,
|
|
217
|
-
failure: `Session error: ${safeErrorMsg}`,
|
|
218
|
-
root_cause: safeErrorMsg,
|
|
219
|
-
provenance: {
|
|
220
|
-
session_id: project?.session_id || `session-${Date.now()}`,
|
|
221
|
-
harness_root: root,
|
|
222
|
-
project_root: directory,
|
|
223
|
-
},
|
|
224
|
-
source: "agent",
|
|
225
|
-
type: "other",
|
|
226
|
-
fix: `Auto-generated - investigate via session_id: ${project?.session_id || "unknown"}`,
|
|
227
|
-
prevention: "curator.js writeMistakeRecord must validate against mistake.schema.json",
|
|
228
|
-
strike: 1,
|
|
229
|
-
status: "active",
|
|
230
|
-
created_at: ts,
|
|
231
|
-
updated_at: ts,
|
|
232
|
-
project: project?.name || path.basename(directory),
|
|
233
|
-
environment_fingerprint: environmentFingerprint,
|
|
234
|
-
}
|
|
235
|
-
const dir = path.join(getMemoryRoot(), "mistakes")
|
|
236
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
237
|
-
const fp = path.join(dir, "mistakes.jsonl")
|
|
238
|
-
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
239
|
-
let entries = []
|
|
240
|
-
try { entries = fs.readFileSync(fp, "utf8").trim().split("\n").filter(Boolean).map(l => JSON.parse(l)) } catch {}
|
|
241
|
-
const idx = entries.findIndex(e => e.id === safeRecord.id)
|
|
242
|
-
if (idx >= 0) entries[idx] = safeRecord; else entries.push(safeRecord)
|
|
243
|
-
const text = entries.map(e => JSON.stringify(e)).join("\n")
|
|
244
|
-
fs.writeFileSync(fp, text ? text + "\n" : "", "utf8")
|
|
245
|
-
curatorLog(`[curator] mistake logged: ${id} - ${safeLogMessage(errorMsg, 80)}`)
|
|
246
|
-
return id
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function writeVerificationReceipt(root, project, directory, checkpointId) {
|
|
250
|
-
const ts = new Date().toISOString()
|
|
251
|
-
const id = `vr_${ts.replace(/[:.]/g, "-")}`
|
|
252
|
-
const artifactPath = path.join(root, "memory", "checkpoints", `${checkpointId}.json`)
|
|
253
|
-
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
254
|
-
const record = {
|
|
255
|
-
id,
|
|
256
|
-
class: "verification_receipt",
|
|
257
|
-
scope: "session",
|
|
258
|
-
summary: `Auto-verification receipt for checkpoint ${checkpointId}`,
|
|
259
|
-
artifact: `openhermes/memory/checkpoints/${checkpointId}.json`,
|
|
260
|
-
artifact_type: "file",
|
|
261
|
-
fingerprint: fingerprintFile(artifactPath) || { path: `openhermes/memory/checkpoints/${checkpointId}.json` },
|
|
262
|
-
environment: {
|
|
263
|
-
cwd: directory,
|
|
264
|
-
os: "win32",
|
|
265
|
-
shell: "cmd.exe",
|
|
266
|
-
provider: "lmstudio"
|
|
267
|
-
},
|
|
268
|
-
method: "manual-inspection",
|
|
269
|
-
command: `curator.js auto-verification on session.idle`,
|
|
270
|
-
result: "pass",
|
|
271
|
-
result_detail: `Checkpoint ${checkpointId} written and indexed. Session completed successfully.`,
|
|
272
|
-
provenance: {
|
|
273
|
-
session_id: project?.session_id || `session-${Date.now()}`,
|
|
274
|
-
harness_root: root,
|
|
275
|
-
project_root: directory,
|
|
276
|
-
},
|
|
277
|
-
confidence: 0.8,
|
|
278
|
-
source: "agent",
|
|
279
|
-
status: "active",
|
|
280
|
-
created_at: ts,
|
|
281
|
-
updated_at: ts,
|
|
282
|
-
project: project?.name || path.basename(directory),
|
|
283
|
-
environment_fingerprint: environmentFingerprint,
|
|
284
|
-
}
|
|
285
|
-
const dir = path.join(root, "memory", "verification_receipts")
|
|
286
|
-
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
287
|
-
atomicWriteJson(path.join(dir, `${id}.json`), safeRecord)
|
|
288
|
-
indexEntry(root, "verification_receipts", safeRecord)
|
|
289
|
-
curatorLog(`[curator] verification_receipt written: ${id}`)
|
|
290
|
-
return id
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
async function handleSessionIdle(directory, project) {
|
|
294
|
-
try {
|
|
295
|
-
const root = getDataRoot()
|
|
296
|
-
const checkpointId = await writeCheckpoint(root, project, directory, "session.idle", null)
|
|
297
|
-
if (checkpointId) {
|
|
298
|
-
writeVerificationReceipt(root, project, directory, checkpointId)
|
|
299
|
-
}
|
|
300
|
-
} catch (err) {
|
|
301
|
-
curatorLog(`[curator] handleSessionIdle error: ${safeLogMessage(err.message)}`)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
async function handleSessionCompacted(directory, project) {
|
|
306
|
-
try {
|
|
307
|
-
const root = getDataRoot()
|
|
308
|
-
const ts = new Date().toISOString()
|
|
309
|
-
updateLoopState(root, {
|
|
310
|
-
status: "compacted",
|
|
311
|
-
last_gate_result: "compaction completed",
|
|
312
|
-
phase: "session.compacted",
|
|
313
|
-
heartbeat_at: ts,
|
|
314
|
-
updated_at: ts,
|
|
315
|
-
})
|
|
316
|
-
} catch (err) {
|
|
317
|
-
curatorLog(`[curator] handleSessionCompacted error: ${safeLogMessage(err.message)}`)
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
async function handleSessionError(directory, project, event) {
|
|
322
|
-
try {
|
|
323
|
-
const root = getDataRoot()
|
|
324
|
-
const ts = new Date().toISOString()
|
|
325
|
-
const errorMsg = typeof event?.error === "object" && event.error !== null
|
|
326
|
-
? (event.error.message || JSON.stringify(event.error).slice(0, 200))
|
|
327
|
-
: (event?.error?.toString() || event?.message || "Unknown error")
|
|
328
|
-
const safeErrorMsg = safeLogMessage(errorMsg, 200)
|
|
329
|
-
updateLoopState(root, {
|
|
330
|
-
status: "error",
|
|
331
|
-
phase: "session.error",
|
|
332
|
-
last_error: safeErrorMsg,
|
|
333
|
-
heartbeat_at: ts,
|
|
334
|
-
updated_at: ts,
|
|
335
|
-
})
|
|
336
|
-
writeMistakeRecord(root, project, directory, event.error || event)
|
|
337
|
-
} catch (err) {
|
|
338
|
-
curatorLog(`[curator] handleSessionError error: ${safeLogMessage(err.message)}`)
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
async function handlePermissionReplied(directory, project, event) {
|
|
343
|
-
try {
|
|
344
|
-
const root = getDataRoot()
|
|
345
|
-
const ts = new Date().toISOString()
|
|
346
|
-
const id = `audit_perm_${ts.replace(/[:.]/g, "-")}`
|
|
347
|
-
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
348
|
-
const record = {
|
|
349
|
-
id,
|
|
350
|
-
class: "audit",
|
|
351
|
-
scope: "harness",
|
|
352
|
-
summary: `Permission reply: ${event.action || "?"} for ${event.tool || "?"}`,
|
|
353
|
-
target: event.tool || "unknown",
|
|
354
|
-
description: `Permission decision: ${event.action || "?"} - tool: ${event.tool || "?"}, pattern: ${event.pattern || "?"}`,
|
|
355
|
-
overall_score: 0,
|
|
356
|
-
checks: [
|
|
357
|
-
{
|
|
358
|
-
name: "permission decision audit",
|
|
359
|
-
status: "pass",
|
|
360
|
-
details: `Permission ${event.action || "?"} logged for tool ${event.tool || "?"}`,
|
|
361
|
-
},
|
|
362
|
-
],
|
|
363
|
-
top_actions: [
|
|
364
|
-
"Auto-recorded permission event — no manual remediation needed.",
|
|
365
|
-
],
|
|
366
|
-
integrity: {
|
|
367
|
-
refs_ok: true,
|
|
368
|
-
provenance_ok: true,
|
|
369
|
-
duplicates_ok: true,
|
|
370
|
-
},
|
|
371
|
-
provenance: {
|
|
372
|
-
session_id: project?.session_id || `session-${Date.now()}`,
|
|
373
|
-
harness_root: root,
|
|
374
|
-
project_root: directory,
|
|
375
|
-
},
|
|
376
|
-
source: "agent",
|
|
377
|
-
status: "closed",
|
|
378
|
-
created_at: ts,
|
|
379
|
-
updated_at: ts,
|
|
380
|
-
project: project?.name || path.basename(directory),
|
|
381
|
-
environment_fingerprint: environmentFingerprint,
|
|
382
|
-
}
|
|
383
|
-
const safeRecord = sanitizeRecord(record, { maxStringLength: 4000 })
|
|
384
|
-
const auditSchema = loadSchema("audit")
|
|
385
|
-
if (auditSchema) {
|
|
386
|
-
const unsupported = findUnsupportedSchemaKeywords(auditSchema)
|
|
387
|
-
if (!unsupported.length) {
|
|
388
|
-
const errors = validateSchema(auditSchema, safeRecord, "$")
|
|
389
|
-
if (errors.length) return
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
const dir = path.join(root, "memory", "audits")
|
|
393
|
-
atomicWriteJson(path.join(dir, `${id}.json`), safeRecord)
|
|
394
|
-
indexEntry(root, "audits", safeRecord)
|
|
395
|
-
curatorLog(`[curator] permission audit logged: ${event.tool} -> ${event.action}`)
|
|
396
|
-
} catch (err) {
|
|
397
|
-
curatorLog(`[curator] handlePermissionReplied error: ${safeLogMessage(err.message)}`)
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
export const CuratorPlugin = async ({ project, directory }) => {
|
|
402
|
-
return {
|
|
403
|
-
event: async ({ event }) => {
|
|
404
|
-
// A1: v1 migration owned by autorecall.mjs — removed to avoid race
|
|
405
|
-
if (event.type === "session.created") {
|
|
406
|
-
lastCheckpoint.ts = 0
|
|
407
|
-
writtenThisSession.length = 0
|
|
408
|
-
try {
|
|
409
|
-
const root = getDataRoot()
|
|
410
|
-
ensureConsistency(root)
|
|
411
|
-
curatorLog("[curator] consistency repair: all memory/runtime dirs verified")
|
|
412
|
-
} catch (err) {
|
|
413
|
-
curatorLog(`[curator] consistency repair failure: ${safeLogMessage(err.message)}`)
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
if (event.type === "session.idle") {
|
|
417
|
-
await handleSessionIdle(directory, project)
|
|
418
|
-
} else if (event.type === "session.compacted") {
|
|
419
|
-
await handleSessionCompacted(directory, project)
|
|
420
|
-
} else if (event.type === "session.error") {
|
|
421
|
-
await handleSessionError(directory, project, event)
|
|
422
|
-
} else if (event.type === "permission.replied") {
|
|
423
|
-
await handlePermissionReplied(directory, project, event)
|
|
424
|
-
} else if (event.type === "command.executed") {
|
|
425
|
-
curatorLog(`[curator] command executed: ${event.command || "?"}`)
|
|
426
|
-
}
|
|
427
|
-
},
|
|
428
|
-
"experimental.session.compacting": async (input, output) => {
|
|
429
|
-
try {
|
|
430
|
-
const root = getDataRoot()
|
|
431
|
-
const projectKey = project?.name || path.basename(directory)
|
|
432
|
-
const checkpointIndex = readJson(path.join(root, "memory", "checkpoints", "index.json"), [])
|
|
433
|
-
const constraintsIndex = readJson(path.join(root, "memory", "constraints", "index.json"), [])
|
|
434
|
-
const environmentFingerprint = buildEnvironmentFingerprint(root, directory, project)
|
|
435
|
-
const preCompactionCheckpointId = await writeCheckpoint(root, project, directory, "experimental.session.compacting", `Pre-compaction checkpoint for ${projectKey}`, { force: true })
|
|
436
|
-
|
|
437
|
-
const latestCheckpoint = Array.isArray(checkpointIndex) && checkpointIndex.length > 0
|
|
438
|
-
? checkpointIndex.sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))[0]
|
|
439
|
-
: null
|
|
440
|
-
|
|
441
|
-
const activeConstraints = Array.isArray(constraintsIndex)
|
|
442
|
-
? constraintsIndex.filter(c => c.status === "active")
|
|
443
|
-
: []
|
|
444
|
-
|
|
445
|
-
const inject = [
|
|
446
|
-
`## OpenHermes State`,
|
|
447
|
-
`- Project: ${projectKey}`,
|
|
448
|
-
`- Latest checkpoint: ${latestCheckpoint ? latestCheckpoint.summary : "none"}`,
|
|
449
|
-
preCompactionCheckpointId ? `- Pre-compaction checkpoint: ${preCompactionCheckpointId}` : null,
|
|
450
|
-
`- Active constraints: ${activeConstraints.length}`,
|
|
451
|
-
`- Memory writes this session: ${writtenThisSession.length}`,
|
|
452
|
-
writtenThisSession.length > 0 ? `- Recent writes: ${writtenThisSession.slice(-3).join(", ")}` : null,
|
|
453
|
-
`- Session hook: experimental.session.compacting`,
|
|
454
|
-
].filter(Boolean).join("\n")
|
|
455
|
-
|
|
456
|
-
const recallCache = readJson(path.join(root, "memory", "recall", "cache.json"), null)
|
|
457
|
-
const cacheMatches = recallCache && recallCache.fingerprint && recallCache.fingerprint.sha256 === environmentFingerprint.sha256
|
|
458
|
-
const cacheFresh = cacheMatches && recallCache.freshness_marker && recallCache.freshness_marker.updated_at
|
|
459
|
-
? (Date.now() - Date.parse(recallCache.freshness_marker.updated_at) <= (recallCache.freshness_marker.ttl_ms || 0))
|
|
460
|
-
: false
|
|
461
|
-
const contextSink = Array.isArray(output.context) ? output.context : (output.context = [])
|
|
462
|
-
if (recallCache && recallCache.context && cacheFresh) {
|
|
463
|
-
const merged = truncateText(`${inject}\n\n${recallCache.context}`, COMPACTION_CONTEXT_LIMIT)
|
|
464
|
-
contextSink.push(merged)
|
|
465
|
-
curatorLog(`[curator] compaction injected harness state + autorecall (${merged.length} chars)`)
|
|
466
|
-
} else {
|
|
467
|
-
contextSink.push(truncateText(inject, COMPACTION_CONTEXT_LIMIT))
|
|
468
|
-
curatorLog(`[curator] compaction injected harness state (stale or missing autorecall cache)`)
|
|
469
|
-
}
|
|
470
|
-
updateLoopState(root, {
|
|
471
|
-
phase: "compress",
|
|
472
|
-
heartbeat_at: new Date().toISOString(),
|
|
473
|
-
status: "active",
|
|
474
|
-
})
|
|
475
|
-
} catch (err) {
|
|
476
|
-
curatorLog(`[curator] compaction error: ${safeLogMessage(err.message)}`)
|
|
477
|
-
const contextSink = Array.isArray(output.context) ? output.context : (output.context = [])
|
|
478
|
-
contextSink.push(truncateText(`## OpenHermes State\n- Project: ${project?.name || path.basename(directory)}\n- Hook: experimental.session.compacting (error state)\n`, COMPACTION_CONTEXT_LIMIT))
|
|
479
|
-
}
|
|
480
|
-
},
|
|
481
|
-
}
|
|
482
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Fix build and TypeScript errors with minimal changes
|
|
3
|
-
agent: build-error-resolver
|
|
4
|
-
subtask: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Build Fix Command
|
|
8
|
-
|
|
9
|
-
Fix build and TypeScript errors with minimal changes: $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
## Current type errors
|
|
12
|
-
|
|
13
|
-
!npx tsc --noEmit 2>&1 || echo "tsc not available or no errors"
|
|
14
|
-
|
|
15
|
-
## Your Task
|
|
16
|
-
|
|
17
|
-
1. **Run type check**: `npx tsc --noEmit`
|
|
18
|
-
2. **Collect all errors**
|
|
19
|
-
3. **Fix errors one by one** with minimal changes
|
|
20
|
-
4. **Verify each fix** doesn't introduce new errors
|
|
21
|
-
5. **Run final check** to confirm all errors resolved
|
|
22
|
-
|
|
23
|
-
## Approach
|
|
24
|
-
|
|
25
|
-
### DO:
|
|
26
|
-
- PASS: Fix type errors with correct types
|
|
27
|
-
- PASS: Add missing imports
|
|
28
|
-
- PASS: Fix syntax errors
|
|
29
|
-
- PASS: Make minimal changes
|
|
30
|
-
- PASS: Preserve existing behavior
|
|
31
|
-
- PASS: Run `tsc --noEmit` after each change
|
|
32
|
-
|
|
33
|
-
### DON'T:
|
|
34
|
-
- FAIL: Refactor code
|
|
35
|
-
- FAIL: Add new features
|
|
36
|
-
- FAIL: Change architecture
|
|
37
|
-
- FAIL: Use `any` type (unless absolutely necessary)
|
|
38
|
-
- FAIL: Add `@ts-ignore` comments
|
|
39
|
-
- FAIL: Change business logic
|
|
40
|
-
|
|
41
|
-
## Common Error Fixes
|
|
42
|
-
|
|
43
|
-
| Error | Fix |
|
|
44
|
-
|-------|-----|
|
|
45
|
-
| Type 'X' is not assignable to type 'Y' | Add correct type annotation |
|
|
46
|
-
| Property 'X' does not exist | Add property to interface or fix property name |
|
|
47
|
-
| Cannot find module 'X' | Install package or fix import path |
|
|
48
|
-
| Argument of type 'X' is not assignable | Cast or fix function signature |
|
|
49
|
-
| Object is possibly 'undefined' | Add null check or optional chaining |
|
|
50
|
-
|
|
51
|
-
## Verification Steps
|
|
52
|
-
|
|
53
|
-
After fixes:
|
|
54
|
-
1. `npx tsc --noEmit` - should show 0 errors
|
|
55
|
-
2. `npm run build` - should succeed
|
|
56
|
-
3. `npm test` - tests should still pass
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
**IMPORTANT**: Focus on fixing errors only. No refactoring, no improvements, no architectural changes. Get the build green with minimal diff.
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Save verification state and progress checkpoint
|
|
3
|
-
agent: OpenHermes
|
|
4
|
-
subtask: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Checkpoint Command
|
|
8
|
-
|
|
9
|
-
Save current verification state and create progress checkpoint: $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
## Your Task
|
|
12
|
-
|
|
13
|
-
Create a snapshot of current progress including:
|
|
14
|
-
|
|
15
|
-
1. **Tests status** - Which tests pass/fail
|
|
16
|
-
2. **Coverage** - Current coverage metrics
|
|
17
|
-
3. **Build status** - Build succeeds or errors
|
|
18
|
-
4. **Code changes** - Summary of modifications
|
|
19
|
-
5. **Next steps** - What remains to be done
|
|
20
|
-
|
|
21
|
-
## Checkpoint Format
|
|
22
|
-
|
|
23
|
-
### Checkpoint: [Timestamp]
|
|
24
|
-
|
|
25
|
-
**Tests**
|
|
26
|
-
- Total: X
|
|
27
|
-
- Passing: Y
|
|
28
|
-
- Failing: Z
|
|
29
|
-
- Coverage: XX%
|
|
30
|
-
|
|
31
|
-
**Build**
|
|
32
|
-
- Status: PASS: Passing / FAIL: Failing
|
|
33
|
-
- Errors: [if any]
|
|
34
|
-
|
|
35
|
-
**Changes Since Last Checkpoint**
|
|
36
|
-
```
|
|
37
|
-
git diff --stat [last-checkpoint-commit]
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
**Completed Tasks**
|
|
41
|
-
- [x] Task 1
|
|
42
|
-
- [x] Task 2
|
|
43
|
-
- [ ] Task 3 (in progress)
|
|
44
|
-
|
|
45
|
-
**Blocking Issues**
|
|
46
|
-
- [Issue description]
|
|
47
|
-
|
|
48
|
-
**Next Steps**
|
|
49
|
-
1. Step 1
|
|
50
|
-
2. Step 2
|
|
51
|
-
|
|
52
|
-
## Usage with Verification Loop
|
|
53
|
-
|
|
54
|
-
Checkpoints integrate with the verification loop:
|
|
55
|
-
|
|
56
|
-
```
|
|
57
|
-
/plan → implement → /checkpoint → /verify → /checkpoint → implement → ...
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Use checkpoints to:
|
|
61
|
-
- Save state before risky changes
|
|
62
|
-
- Track progress through phases
|
|
63
|
-
- Enable rollback if needed
|
|
64
|
-
- Document verification points
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
**TIP**: Create checkpoints at natural breakpoints: after each phase, before major refactoring, after fixing critical bugs.
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Review code for quality, security, and maintainability
|
|
3
|
-
agent: code-reviewer
|
|
4
|
-
subtask: true
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Code Review Command
|
|
8
|
-
|
|
9
|
-
Review code changes for quality, security, and maintainability: $ARGUMENTS
|
|
10
|
-
|
|
11
|
-
## Changed files
|
|
12
|
-
|
|
13
|
-
!git diff --name-only HEAD 2>&1 || echo "No git repo or no changes"
|
|
14
|
-
|
|
15
|
-
## Your Task
|
|
16
|
-
|
|
17
|
-
1. **Analyze each changed file** for issues
|
|
18
|
-
3. **Generate structured report**
|
|
19
|
-
4. **Provide actionable recommendations**
|
|
20
|
-
|
|
21
|
-
## Check Categories
|
|
22
|
-
|
|
23
|
-
### Security Issues (CRITICAL)
|
|
24
|
-
- [ ] Hardcoded credentials, API keys, tokens
|
|
25
|
-
- [ ] SQL injection vulnerabilities
|
|
26
|
-
- [ ] XSS vulnerabilities
|
|
27
|
-
- [ ] Missing input validation
|
|
28
|
-
- [ ] Insecure dependencies
|
|
29
|
-
- [ ] Path traversal risks
|
|
30
|
-
- [ ] Authentication/authorization flaws
|
|
31
|
-
|
|
32
|
-
### Code Quality (HIGH)
|
|
33
|
-
- [ ] Functions > 50 lines
|
|
34
|
-
- [ ] Files > 800 lines
|
|
35
|
-
- [ ] Nesting depth > 4 levels
|
|
36
|
-
- [ ] Missing error handling
|
|
37
|
-
- [ ] console.log statements
|
|
38
|
-
- [ ] TODO/FIXME comments
|
|
39
|
-
- [ ] Missing JSDoc for public APIs
|
|
40
|
-
|
|
41
|
-
### Best Practices (MEDIUM)
|
|
42
|
-
- [ ] Mutation patterns (use immutable instead)
|
|
43
|
-
- [ ] Unnecessary complexity
|
|
44
|
-
- [ ] Missing tests for new code
|
|
45
|
-
- [ ] Accessibility issues (a11y)
|
|
46
|
-
- [ ] Performance concerns
|
|
47
|
-
|
|
48
|
-
### Style (LOW)
|
|
49
|
-
- [ ] Inconsistent naming
|
|
50
|
-
- [ ] Missing type annotations
|
|
51
|
-
- [ ] Formatting issues
|
|
52
|
-
|
|
53
|
-
## Report Format
|
|
54
|
-
|
|
55
|
-
For each issue found:
|
|
56
|
-
|
|
57
|
-
```
|
|
58
|
-
**[SEVERITY]** file.ts:123
|
|
59
|
-
Issue: [Description]
|
|
60
|
-
Fix: [How to fix]
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
## Decision
|
|
64
|
-
|
|
65
|
-
- **CRITICAL or HIGH issues**: Block commit, require fixes
|
|
66
|
-
- **MEDIUM issues**: Recommend fixes before merge
|
|
67
|
-
- **LOW issues**: Optional improvements
|
|
68
|
-
|
|
69
|
-
---
|
|
70
|
-
|
|
71
|
-
**IMPORTANT**: Never approve code with security vulnerabilities!
|