openhermes 2.8.0 → 4.0.0
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 +157 -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
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: verification-loop
|
|
3
|
-
description: "A comprehensive verification system for Claude Code sessions."
|
|
4
|
-
origin: ECC
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Verification Loop Skill
|
|
8
|
-
|
|
9
|
-
A comprehensive verification system for Claude Code sessions.
|
|
10
|
-
|
|
11
|
-
## When to Use
|
|
12
|
-
|
|
13
|
-
Invoke this skill:
|
|
14
|
-
- After completing a feature or significant code change
|
|
15
|
-
- Before creating a PR
|
|
16
|
-
- When you want to ensure quality gates pass
|
|
17
|
-
- After refactoring
|
|
18
|
-
|
|
19
|
-
## Verification Phases
|
|
20
|
-
|
|
21
|
-
### Phase 1: Build Verification
|
|
22
|
-
```bash
|
|
23
|
-
# Check if project builds
|
|
24
|
-
npm run build 2>&1 | tail -20
|
|
25
|
-
# OR
|
|
26
|
-
pnpm build 2>&1 | tail -20
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
If build fails, STOP and fix before continuing.
|
|
30
|
-
|
|
31
|
-
### Phase 2: Type Check
|
|
32
|
-
```bash
|
|
33
|
-
# TypeScript projects
|
|
34
|
-
npx tsc --noEmit 2>&1 | head -30
|
|
35
|
-
|
|
36
|
-
# Python projects
|
|
37
|
-
pyright . 2>&1 | head -30
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
Report all type errors. Fix critical ones before continuing.
|
|
41
|
-
|
|
42
|
-
### Phase 3: Lint Check
|
|
43
|
-
```bash
|
|
44
|
-
# JavaScript/TypeScript
|
|
45
|
-
npm run lint 2>&1 | head -30
|
|
46
|
-
|
|
47
|
-
# Python
|
|
48
|
-
ruff check . 2>&1 | head -30
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Phase 4: Test Suite
|
|
52
|
-
```bash
|
|
53
|
-
# Run tests with coverage
|
|
54
|
-
npm run test -- --coverage 2>&1 | tail -50
|
|
55
|
-
|
|
56
|
-
# Check coverage threshold
|
|
57
|
-
# Target: 80% minimum
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
Report:
|
|
61
|
-
- Total tests: X
|
|
62
|
-
- Passed: X
|
|
63
|
-
- Failed: X
|
|
64
|
-
- Coverage: X%
|
|
65
|
-
|
|
66
|
-
### Phase 5: Security Scan
|
|
67
|
-
```bash
|
|
68
|
-
# Check for secrets
|
|
69
|
-
grep -rn "sk-" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
|
|
70
|
-
grep -rn "api_key" --include="*.ts" --include="*.js" . 2>/dev/null | head -10
|
|
71
|
-
|
|
72
|
-
# Check for console.log
|
|
73
|
-
grep -rn "console.log" --include="*.ts" --include="*.tsx" src/ 2>/dev/null | head -10
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Phase 6: Diff Review
|
|
77
|
-
```bash
|
|
78
|
-
# Show what changed
|
|
79
|
-
git diff --stat
|
|
80
|
-
git diff HEAD~1 --name-only
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
Review each changed file for:
|
|
84
|
-
- Unintended changes
|
|
85
|
-
- Missing error handling
|
|
86
|
-
- Potential edge cases
|
|
87
|
-
|
|
88
|
-
## Output Format
|
|
89
|
-
|
|
90
|
-
After running all phases, produce a verification report:
|
|
91
|
-
|
|
92
|
-
```
|
|
93
|
-
VERIFICATION REPORT
|
|
94
|
-
==================
|
|
95
|
-
|
|
96
|
-
Build: [PASS/FAIL]
|
|
97
|
-
Types: [PASS/FAIL] (X errors)
|
|
98
|
-
Lint: [PASS/FAIL] (X warnings)
|
|
99
|
-
Tests: [PASS/FAIL] (X/Y passed, Z% coverage)
|
|
100
|
-
Security: [PASS/FAIL] (X issues)
|
|
101
|
-
Diff: [X files changed]
|
|
102
|
-
|
|
103
|
-
Overall: [READY/NOT READY] for PR
|
|
104
|
-
|
|
105
|
-
Issues to Fix:
|
|
106
|
-
1. ...
|
|
107
|
-
2. ...
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Continuous Mode
|
|
111
|
-
|
|
112
|
-
For long sessions, run verification every 15 minutes or after major changes:
|
|
113
|
-
|
|
114
|
-
```markdown
|
|
115
|
-
Set a mental checkpoint:
|
|
116
|
-
- After completing each function
|
|
117
|
-
- After finishing a component
|
|
118
|
-
- Before moving to next task
|
|
119
|
-
|
|
120
|
-
Run: /verify
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## Integration with Hooks
|
|
124
|
-
|
|
125
|
-
This skill complements PostToolUse hooks but provides deeper verification.
|
|
126
|
-
Hooks catch issues immediately; this skill provides comprehensive review.
|
package/lib/ambient-memory.mjs
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
import fs from "node:fs"
|
|
3
|
-
import { readJson, readJsonl, truncateText } from "./hardening.mjs"
|
|
4
|
-
import { getMemoryRoot, getRecallRoot, getDataRoot } from "./paths.mjs"
|
|
5
|
-
import { scoreRelevance } from "./search.mjs"
|
|
6
|
-
|
|
7
|
-
const PLURALS = { audit: "audits", checkpoint: "checkpoints", mistake: "mistakes", instinct: "instincts", decision: "decisions", constraint: "constraints", backlog: "backlog", verification_receipt: "verification_receipts" }
|
|
8
|
-
const CLASSES = ["audit", "checkpoint", "mistake", "instinct", "decision", "constraint", "backlog", "verification_receipt"]
|
|
9
|
-
|
|
10
|
-
const MEMORY_QUERY_PATTERNS = [
|
|
11
|
-
/where\s+(did|do)\s+we\s+(leave\s+off|left\s+off)/i,
|
|
12
|
-
/where\s+were\s+we/i,
|
|
13
|
-
/what\s+(was|is)\s+(the\s+)?(last|latest|most\s+recent)\s+(checkpoint|decision|constraint|feature|task|change|issue|fix|bug|request|request)/i,
|
|
14
|
-
/remind\s+me\s+(about|of)/i,
|
|
15
|
-
/what\s+were\s+we\s+(working\s+on|doing)/i,
|
|
16
|
-
/what\s+(happened|changed)\s+(last|yesterday|before|previously)/i,
|
|
17
|
-
/previous\s+session/i,
|
|
18
|
-
/last\s+time/i,
|
|
19
|
-
/recap/i,
|
|
20
|
-
/what\s+is\s+(the\s+)?(status|state)\s+of/i,
|
|
21
|
-
/did\s+we\s+(decide|agree|finish|resolve|discuss)/i,
|
|
22
|
-
/where\s+(did|do)\s+i\s+(leave\s+off|left\s+off)/i,
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
function hasExpired(r) {
|
|
26
|
-
if (r?.status === "expired" || r?.status === "decayed") return true
|
|
27
|
-
if (r?.decay_at && Date.parse(r.decay_at) < Date.now()) return true
|
|
28
|
-
if (r?.expires_at && Date.parse(r.expires_at) < Date.now()) return true
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function filterActive(entries) { return entries.filter(e => !hasExpired(e)) }
|
|
33
|
-
|
|
34
|
-
function detectMemoryQuery(text) {
|
|
35
|
-
return MEMORY_QUERY_PATTERNS.some(p => p.test(text))
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function extractSearchTerms(text) {
|
|
39
|
-
const cleaned = text.replace(/<[^>]+>/g, "").replace(/[^\w\s-]/g, " ").trim()
|
|
40
|
-
return cleaned.slice(0, 200)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function classDir(cls) { return path.join(getMemoryRoot(), PLURALS[cls]) }
|
|
44
|
-
|
|
45
|
-
function readAllRecords() {
|
|
46
|
-
const records = []
|
|
47
|
-
for (const cls of CLASSES) {
|
|
48
|
-
if (cls === "mistake") {
|
|
49
|
-
for (const m of readJsonl(path.join(classDir(cls), "mistakes.jsonl"))) {
|
|
50
|
-
if (!hasExpired(m)) records.push({ ...m, _cls: cls })
|
|
51
|
-
}
|
|
52
|
-
} else {
|
|
53
|
-
const dir = classDir(cls)
|
|
54
|
-
const index = readJson(path.join(dir, "index.json"), [])
|
|
55
|
-
if (!Array.isArray(index)) continue
|
|
56
|
-
for (const entry of index) {
|
|
57
|
-
if (!hasExpired(entry)) {
|
|
58
|
-
records.push({ ...entry, _cls: cls })
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return records
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function autoSearch(query) {
|
|
67
|
-
const q = (query || "").trim()
|
|
68
|
-
if (!q) return []
|
|
69
|
-
|
|
70
|
-
const records = readAllRecords()
|
|
71
|
-
const scored = records
|
|
72
|
-
.map(r => ({ ...r, _score: scoreRelevance(r, q, "") }))
|
|
73
|
-
.filter(r => r._score > 0)
|
|
74
|
-
.sort((a, b) => b._score - a._score)
|
|
75
|
-
.slice(0, 5)
|
|
76
|
-
|
|
77
|
-
return scored
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function buildMemoryBlock() {
|
|
81
|
-
const parts = []
|
|
82
|
-
|
|
83
|
-
const ckIndex = readJson(path.join(getMemoryRoot(), "checkpoints", "index.json"), [])
|
|
84
|
-
if (Array.isArray(ckIndex) && ckIndex.length > 0) {
|
|
85
|
-
const latest = [...ckIndex].sort((a, b) => new Date(b.updated_at) - new Date(a.updated_at))[0]
|
|
86
|
-
if (latest) {
|
|
87
|
-
const cp = readJson(path.join(getMemoryRoot(), "checkpoints", `${latest.id}.json`), null)
|
|
88
|
-
if (cp) {
|
|
89
|
-
parts.push(`Checkpoint: ${cp.summary || "N/A"}`)
|
|
90
|
-
if (cp.mission) parts.push(`Mission: ${truncateText(cp.mission, 200)}`)
|
|
91
|
-
if (Array.isArray(cp.next_actions) && cp.next_actions.length) parts.push(`Next: ${cp.next_actions.slice(0, 2).join("; ")}`)
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const ctIndex = readJson(path.join(getMemoryRoot(), "constraints", "index.json"), [])
|
|
97
|
-
if (Array.isArray(ctIndex)) {
|
|
98
|
-
const active = ctIndex.filter(e => e.status === "active")
|
|
99
|
-
if (active.length) parts.push(`Constraints (${active.length} active)`)
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const dcIndex = readJson(path.join(getMemoryRoot(), "decisions", "index.json"), [])
|
|
103
|
-
if (Array.isArray(dcIndex)) {
|
|
104
|
-
const recent = filterActive(dcIndex).slice(0, 2)
|
|
105
|
-
if (recent.length) parts.push(`Recent decisions: ${recent.map(d => truncateText(d.summary || "", 80)).join(" | ")}`)
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const bkIndex = readJson(path.join(getMemoryRoot(), "backlog", "index.json"), [])
|
|
109
|
-
if (Array.isArray(bkIndex)) {
|
|
110
|
-
const open = bkIndex.filter(e => e.status === "open")
|
|
111
|
-
if (open.length) parts.push(`Backlog: ${open.length} open`)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
return parts.length ? parts.join("\n") : "No active memory records found."
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export const AmbientMemoryPlugin = async () => ({
|
|
118
|
-
"experimental.chat.messages.transform": async (_input, output) => {
|
|
119
|
-
if (!output.messages?.length) return
|
|
120
|
-
|
|
121
|
-
const firstUser = output.messages.find(m => m.info?.role === "user")
|
|
122
|
-
if (!firstUser?.parts?.length) return
|
|
123
|
-
|
|
124
|
-
const hasMemoryTag = firstUser.parts.some(p => p.type === "text" && p.text.includes("OPENHERMES_MEMORY"))
|
|
125
|
-
|
|
126
|
-
if (!hasMemoryTag) {
|
|
127
|
-
const block = buildMemoryBlock()
|
|
128
|
-
const tag = `<OPENHERMES_MEMORY>\n${block}\n</OPENHERMES_MEMORY>`
|
|
129
|
-
const textPart = firstUser.parts.find(p => p.type === "text")
|
|
130
|
-
if (textPart) {
|
|
131
|
-
textPart.text = `${tag}\n\n${textPart.text}`
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const lastUser = [...output.messages].reverse().find(m => m.info?.role === "user")
|
|
136
|
-
if (!lastUser) return
|
|
137
|
-
|
|
138
|
-
const text = lastUser.parts?.find(p => p.type === "text")?.text || ""
|
|
139
|
-
if (!text || !detectMemoryQuery(text)) return
|
|
140
|
-
|
|
141
|
-
const terms = extractSearchTerms(text)
|
|
142
|
-
if (!terms) return
|
|
143
|
-
|
|
144
|
-
const results = autoSearch(terms)
|
|
145
|
-
if (!results.length) return
|
|
146
|
-
|
|
147
|
-
const contextBlock = [
|
|
148
|
-
`<memory-context>`,
|
|
149
|
-
`[System note: auto-retrieved from durable memory — NOT new user input]`,
|
|
150
|
-
...results.slice(0, 3).map(r => {
|
|
151
|
-
const cls = r.class || "?";
|
|
152
|
-
const label = `${cls}:${r.id}`
|
|
153
|
-
return `- ${label}: ${truncateText(r.summary || "", 120)}`
|
|
154
|
-
}),
|
|
155
|
-
results.length > 3 ? ` ... and ${results.length - 3} more results` : null,
|
|
156
|
-
`</memory-context>`,
|
|
157
|
-
].filter(Boolean).join("\n")
|
|
158
|
-
|
|
159
|
-
const idx = output.messages.indexOf(lastUser)
|
|
160
|
-
if (idx > 0) {
|
|
161
|
-
output.messages.splice(idx, 0, {
|
|
162
|
-
parts: [{ type: "text", text: contextBlock }],
|
|
163
|
-
info: { role: "system" },
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
})
|
package/lib/handoff.mjs
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
// OpenHermes Agent Handoff — lightweight helper library
|
|
2
|
-
// Convention-based, no runtime deps. Agents import and call.
|
|
3
|
-
|
|
4
|
-
let _idSeq = 0
|
|
5
|
-
|
|
6
|
-
function nextId() {
|
|
7
|
-
_idSeq++
|
|
8
|
-
return `ho_${Date.now().toString(36)}_${_idSeq}`
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function clamp(v, lo, hi) { return Math.max(lo, Math.min(hi, v)) }
|
|
12
|
-
|
|
13
|
-
// Estimate task complexity from file count and description.
|
|
14
|
-
export function assessComplexity(fileCount = 1, description = "", patterns = []) {
|
|
15
|
-
const pScore = Array.isArray(patterns) ? patterns.length : 0
|
|
16
|
-
const level = fileCount > 50 || pScore > 5 ? "very-large"
|
|
17
|
-
: fileCount > 10 || pScore > 2 ? "hard"
|
|
18
|
-
: fileCount > 2 || pScore > 0 ? "medium"
|
|
19
|
-
: "easy"
|
|
20
|
-
return {
|
|
21
|
-
level,
|
|
22
|
-
fileCount: clamp(fileCount, 0, 9999),
|
|
23
|
-
patterns: pScore,
|
|
24
|
-
strategy: level === "easy" ? "direct"
|
|
25
|
-
: level === "medium" ? "sequential-or-fan-out"
|
|
26
|
-
: level === "hard" ? "sequential-multi"
|
|
27
|
-
: "fan-out"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Build a structured handoff request string for the `task` tool prompt.
|
|
32
|
-
export function handoffRequest(opts) {
|
|
33
|
-
const {
|
|
34
|
-
agent,
|
|
35
|
-
phase = "execute",
|
|
36
|
-
context = "",
|
|
37
|
-
goal = "",
|
|
38
|
-
expected = "",
|
|
39
|
-
permissions = "",
|
|
40
|
-
limits = "",
|
|
41
|
-
complexity,
|
|
42
|
-
} = opts || {}
|
|
43
|
-
const id = nextId()
|
|
44
|
-
const c = complexity || assessComplexity()
|
|
45
|
-
const lines = [
|
|
46
|
-
`## HANDOFF REQUEST`,
|
|
47
|
-
`Agent: ${agent}`,
|
|
48
|
-
`Task ID: ${id}`,
|
|
49
|
-
`Phase: ${phase}`,
|
|
50
|
-
`Complexity: ${c.level}`,
|
|
51
|
-
``,
|
|
52
|
-
`### Context`,
|
|
53
|
-
String(context).trim() || "(no context provided)",
|
|
54
|
-
``,
|
|
55
|
-
`### Goal`,
|
|
56
|
-
String(goal).trim() || "(no goal provided)",
|
|
57
|
-
``,
|
|
58
|
-
`### Expected Output`,
|
|
59
|
-
String(expected).trim() || "Return structured result with status, summary, details, receipts, next, learning.",
|
|
60
|
-
``,
|
|
61
|
-
`### Permissions`,
|
|
62
|
-
String(permissions).trim() || "(default permissions apply)",
|
|
63
|
-
``,
|
|
64
|
-
`### Limits`,
|
|
65
|
-
String(limits).trim() || "(no explicit limits)",
|
|
66
|
-
``,
|
|
67
|
-
`### Handoff Result Format`,
|
|
68
|
-
`When done, return:`,
|
|
69
|
-
`## HANDOFF RESULT`,
|
|
70
|
-
`Status: success | failure | partial`,
|
|
71
|
-
`Task ID: ${id}`,
|
|
72
|
-
`### Summary`,
|
|
73
|
-
`### Details`,
|
|
74
|
-
`### Receipts`,
|
|
75
|
-
`### Next`,
|
|
76
|
-
`### Learning`,
|
|
77
|
-
].join("\n")
|
|
78
|
-
return { id, prompt: lines }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Validate a subagent's result string has required sections.
|
|
82
|
-
export function parseHandoffResult(text) {
|
|
83
|
-
if (!text || typeof text !== "string") return { status: "invalid", error: "no result text" }
|
|
84
|
-
const statusMatch = text.match(/Status:\s*(success|failure|partial)/i)
|
|
85
|
-
const hasSummary = /### Summary/i.test(text)
|
|
86
|
-
const hasDetails = /### Details/i.test(text)
|
|
87
|
-
const hasReceipts = /### Receipts/i.test(text)
|
|
88
|
-
const summaryMatch = text.match(/### Summary\s*\n\s*(.+)/i)
|
|
89
|
-
return {
|
|
90
|
-
status: statusMatch ? statusMatch[1].toLowerCase() : "unknown",
|
|
91
|
-
summary: summaryMatch ? summaryMatch[1].trim() : "",
|
|
92
|
-
hasSummary,
|
|
93
|
-
hasDetails,
|
|
94
|
-
hasReceipts,
|
|
95
|
-
valid: !!(statusMatch && hasSummary),
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Agent capability/role definitions for reference.
|
|
100
|
-
export const AGENT_ROLES = {
|
|
101
|
-
OpenHermes: { tier: 3, edit: true, exec: true, desc: "Primary agent — all tools, all permissions" },
|
|
102
|
-
architect: { tier: 1, edit: false, exec: false, desc: "System architecture design" },
|
|
103
|
-
planner: { tier: 1, edit: false, exec: false, desc: "Feature/refactor planning" },
|
|
104
|
-
"code-reviewer": { tier: 1, edit: false, exec: false, desc: "Code quality review" },
|
|
105
|
-
"security-reviewer": { tier: 1, edit: false, exec: false, desc: "Security audit (report only, no patches)" },
|
|
106
|
-
explore: { tier: 1, edit: false, exec: false, desc: "Read-only codebase exploration" },
|
|
107
|
-
"build-error-resolver": { tier: 2, edit: true, exec: true, desc: "Build/type error fixes" },
|
|
108
|
-
"doc-updater": { tier: 2, edit: true, exec: true, desc: "Doc/codemap updates" },
|
|
109
|
-
"refactor-cleaner": { tier: 2, edit: true, exec: true, desc: "Dead code cleanup" },
|
|
110
|
-
"tdd-guide": { tier: 2, edit: true, exec: true, desc: "TDD red-green-refactor" },
|
|
111
|
-
"loop-operator": { tier: 3, edit: true, exec: true, desc: "Managed autonomous loops" },
|
|
112
|
-
"e2e-runner": { tier: 3, edit: true, exec: true, desc: "Playwright E2E tests" },
|
|
113
|
-
"docs-lookup": { tier: 1, edit: false, exec: true, desc: "MCP doc lookup" },
|
|
114
|
-
"harness-optimizer": { tier: 1, edit: false, exec: true, desc: "Harness config audit" },
|
|
115
|
-
"review-database": { tier: 1, edit: false, exec: true, desc: "PostgreSQL review" },
|
|
116
|
-
"review-go": { tier: 1, edit: false, exec: true, desc: "Go code review" },
|
|
117
|
-
"build-go": { tier: 2, edit: true, exec: true, desc: "Go build fix" },
|
|
118
|
-
"review-java": { tier: 1, edit: false, exec: true, desc: "Java review" },
|
|
119
|
-
"build-java": { tier: 2, edit: true, exec: true, desc: "Java build fix" },
|
|
120
|
-
"review-kotlin": { tier: 1, edit: false, exec: true, desc: "Kotlin review" },
|
|
121
|
-
"build-kotlin": { tier: 2, edit: true, exec: true, desc: "Kotlin build fix" },
|
|
122
|
-
"review-cpp": { tier: 1, edit: false, exec: true, desc: "C++ review" },
|
|
123
|
-
"build-cpp": { tier: 2, edit: true, exec: true, desc: "C++ build fix" },
|
|
124
|
-
"review-rust": { tier: 1, edit: false, exec: true, desc: "Rust review" },
|
|
125
|
-
"build-rust": { tier: 2, edit: true, exec: true, desc: "Rust build fix" },
|
|
126
|
-
"review-python": { tier: 1, edit: false, exec: true, desc: "Python review" },
|
|
127
|
-
"general": { tier: 2, edit: true, exec: true, desc: "General purpose" },
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Suggest best agent for a task type.
|
|
131
|
-
export function suggestAgent(taskType) {
|
|
132
|
-
const map = {
|
|
133
|
-
architecture: "architect",
|
|
134
|
-
plan: "planner",
|
|
135
|
-
"code-review": "code-reviewer",
|
|
136
|
-
security: "security-reviewer",
|
|
137
|
-
explore: "explore",
|
|
138
|
-
"build-fix": "build-error-resolver",
|
|
139
|
-
docs: "doc-updater",
|
|
140
|
-
"dead-code": "refactor-cleaner",
|
|
141
|
-
tdd: "tdd-guide",
|
|
142
|
-
e2e: "e2e-runner",
|
|
143
|
-
"doc-lookup": "docs-lookup",
|
|
144
|
-
"db-review": "review-database",
|
|
145
|
-
"go-review": "review-go",
|
|
146
|
-
"go-build": "build-go",
|
|
147
|
-
"java-review": "review-java",
|
|
148
|
-
"java-build": "build-java",
|
|
149
|
-
"kotlin-review": "review-kotlin",
|
|
150
|
-
"kotlin-build": "build-kotlin",
|
|
151
|
-
"cpp-review": "review-cpp",
|
|
152
|
-
"cpp-build": "build-cpp",
|
|
153
|
-
"rust-review": "review-rust",
|
|
154
|
-
"rust-build": "build-rust",
|
|
155
|
-
"python-review": "review-python",
|
|
156
|
-
loop: "loop-operator",
|
|
157
|
-
"harness-audit": "harness-optimizer",
|
|
158
|
-
}
|
|
159
|
-
return map[taskType] || null
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Quick permission check: can agent X do action Y?
|
|
163
|
-
export function canAgent(agentName, action) {
|
|
164
|
-
const role = AGENT_ROLES[agentName]
|
|
165
|
-
if (!role) return false
|
|
166
|
-
if (action === "edit") return role.edit
|
|
167
|
-
if (action === "exec" || action === "bash") return role.exec
|
|
168
|
-
if (action === "read") return true
|
|
169
|
-
if (action === "delegate") return role.tier >= 1
|
|
170
|
-
return false
|
|
171
|
-
}
|
package/lib/hardening.mjs
DELETED
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import crypto from "node:crypto"
|
|
2
|
-
import fs from "node:fs"
|
|
3
|
-
import os from "node:os"
|
|
4
|
-
import path from "node:path"
|
|
5
|
-
|
|
6
|
-
const SECRET_KEY_PATTERN = /(token|secret|password|passwd|passphrase|pwd|credential|auth|api[-_ ]?key|access[-_ ]?key|refresh[-_ ]?token|authorization|cookie|bearer)/i
|
|
7
|
-
const TEXT_REDACTIONS = [
|
|
8
|
-
[/\bsk-[A-Za-z0-9]{16,}\b/g, "[REDACTED]"],
|
|
9
|
-
[/\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g, "[REDACTED]"],
|
|
10
|
-
[/\bgithub_pat_[A-Za-z0-9_]+\b/g, "[REDACTED]"],
|
|
11
|
-
[/\bxox[baprs]-[A-Za-z0-9-]+\b/g, "[REDACTED]"],
|
|
12
|
-
[/\bBearer\s+[A-Za-z0-9._~+/=-]{8,}\b/gi, "Bearer [REDACTED]"],
|
|
13
|
-
[/\b[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]{20,}\b/g, "[REDACTED]"],
|
|
14
|
-
]
|
|
15
|
-
|
|
16
|
-
function isPlainObject(value) {
|
|
17
|
-
return !!value && typeof value === "object" && !Array.isArray(value)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function truncateText(text, limit = 12000) {
|
|
21
|
-
const value = String(text ?? "")
|
|
22
|
-
if (limit <= 0 || value.length <= limit) return value
|
|
23
|
-
const suffix = "...[truncated]"
|
|
24
|
-
const sliceLength = Math.max(0, limit - suffix.length)
|
|
25
|
-
return value.slice(0, sliceLength) + suffix
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function redactSensitiveText(text) {
|
|
29
|
-
let output = String(text ?? "")
|
|
30
|
-
for (const [pattern, replacement] of TEXT_REDACTIONS) {
|
|
31
|
-
output = output.replace(pattern, replacement)
|
|
32
|
-
}
|
|
33
|
-
return output
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function sanitizeValue(value, key = "", options = {}) {
|
|
37
|
-
const maxStringLength = Number.isFinite(options.maxStringLength) ? options.maxStringLength : 12000
|
|
38
|
-
if (value === null || value === undefined) return value
|
|
39
|
-
if (typeof value === "string") return truncateText(redactSensitiveText(value), maxStringLength)
|
|
40
|
-
if (typeof value === "number" || typeof value === "boolean") return value
|
|
41
|
-
if (Array.isArray(value)) return value.map(item => sanitizeValue(item, key, options))
|
|
42
|
-
if (isPlainObject(value)) {
|
|
43
|
-
const redacted = {}
|
|
44
|
-
for (const [childKey, childValue] of Object.entries(value)) {
|
|
45
|
-
redacted[childKey] = SECRET_KEY_PATTERN.test(childKey)
|
|
46
|
-
? "[REDACTED]"
|
|
47
|
-
: sanitizeValue(childValue, childKey, options)
|
|
48
|
-
}
|
|
49
|
-
return redacted
|
|
50
|
-
}
|
|
51
|
-
return truncateText(redactSensitiveText(String(value)), maxStringLength)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function sanitizeRecord(record, options = {}) {
|
|
55
|
-
return sanitizeValue(record, "", options)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function fingerprintEnvironment(input = {}) {
|
|
59
|
-
const fingerprint = {
|
|
60
|
-
cwd: path.resolve(input.cwd || process.cwd()),
|
|
61
|
-
harness_root: input.harnessRoot ? path.resolve(input.harnessRoot) : null,
|
|
62
|
-
project_root: input.projectRoot ? path.resolve(input.projectRoot) : null,
|
|
63
|
-
project: input.project || null,
|
|
64
|
-
session_id: input.sessionId || null,
|
|
65
|
-
os: process.platform,
|
|
66
|
-
release: os.release(),
|
|
67
|
-
arch: process.arch,
|
|
68
|
-
shell: process.env.ComSpec || process.env.COMSPEC || "cmd.exe",
|
|
69
|
-
provider: process.env.OPENCODE_PROVIDER || "lmstudio",
|
|
70
|
-
model: process.env.OPENCODE_MODEL || null,
|
|
71
|
-
}
|
|
72
|
-
const sha256 = crypto.createHash("sha256").update(JSON.stringify(fingerprint)).digest("hex")
|
|
73
|
-
return { ...fingerprint, sha256 }
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
function fingerprintFile(filePath) {
|
|
77
|
-
try {
|
|
78
|
-
const stat = fs.statSync(filePath)
|
|
79
|
-
const content = fs.readFileSync(filePath)
|
|
80
|
-
return {
|
|
81
|
-
path: path.normalize(filePath),
|
|
82
|
-
mtime: stat.mtime.toISOString(),
|
|
83
|
-
size: stat.size,
|
|
84
|
-
sha256: crypto.createHash("sha256").update(content).digest("hex"),
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
return null
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function atomicWriteJson(filePath, data) {
|
|
92
|
-
const dir = path.dirname(filePath)
|
|
93
|
-
fs.mkdirSync(dir, { recursive: true })
|
|
94
|
-
const tmpPath = path.join(dir, `.${path.basename(filePath)}.${process.pid}.${Date.now()}.tmp`)
|
|
95
|
-
const payload = `${JSON.stringify(data, null, 2)}\n`
|
|
96
|
-
fs.writeFileSync(tmpPath, payload, "utf8")
|
|
97
|
-
try {
|
|
98
|
-
fs.renameSync(tmpPath, filePath)
|
|
99
|
-
} catch (err) {
|
|
100
|
-
fs.copyFileSync(tmpPath, filePath)
|
|
101
|
-
fs.rmSync(tmpPath, { force: true })
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function isTruthy(value) {
|
|
106
|
-
return /^(1|true|yes|on)$/i.test(String(value || ""))
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function readJson(fp, fallback) {
|
|
110
|
-
try { return JSON.parse(fs.readFileSync(fp, "utf8")) } catch { return fallback }
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function readJsonl(fp) {
|
|
114
|
-
try {
|
|
115
|
-
const results = []
|
|
116
|
-
for (const l of fs.readFileSync(fp, "utf8").trim().split("\n").filter(Boolean)) {
|
|
117
|
-
try { results.push(JSON.parse(l)) } catch {}
|
|
118
|
-
}
|
|
119
|
-
return results
|
|
120
|
-
} catch { return [] }
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function buildEnvironmentFingerprint(root, directory, project) {
|
|
124
|
-
return fingerprintEnvironment({
|
|
125
|
-
cwd: directory,
|
|
126
|
-
harnessRoot: root,
|
|
127
|
-
projectRoot: directory,
|
|
128
|
-
project: project?.name || path.basename(directory),
|
|
129
|
-
sessionId: project?.session_id || null,
|
|
130
|
-
})
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function toDisplayPath(absPath) {
|
|
134
|
-
if (!absPath) return ''
|
|
135
|
-
const normalized = absPath.replace(/\\/g, '/')
|
|
136
|
-
const idx = normalized.lastIndexOf('/openhermes/')
|
|
137
|
-
if (idx !== -1) return 'openhermes' + normalized.slice(idx + 11)
|
|
138
|
-
const parts = normalized.split('/').filter(Boolean)
|
|
139
|
-
return parts.slice(-2).join('/')
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function branded(prefix, message) {
|
|
143
|
-
return `OpenHermes${prefix ? ` ${prefix}` : ''}: ${message}`
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export { atomicWriteJson, branded, buildEnvironmentFingerprint, fingerprintEnvironment, fingerprintFile, isPlainObject, isTruthy, readJson, readJsonl, redactSensitiveText, sanitizeRecord, toDisplayPath, truncateText }
|