@gamaze/hicortex 0.3.17 → 0.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/README.md +102 -78
- package/dist/cli.js +10 -4
- package/dist/consolidate.js +8 -1
- package/dist/init.d.ts +3 -1
- package/dist/init.js +375 -55
- package/dist/license.js +1 -0
- package/dist/llm.d.ts +1 -0
- package/dist/llm.js +49 -4
- package/dist/mcp-server.js +63 -3
- package/dist/nightly.js +172 -3
- package/dist/prompts.d.ts +1 -1
- package/dist/prompts.js +41 -13
- package/dist/types.d.ts +1 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,137 +1,159 @@
|
|
|
1
|
-
# @gamaze/hicortex —
|
|
1
|
+
# @gamaze/hicortex — Self-Learning Memory for AI Agents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Your agents learn from every session — successes and mistakes. Hicortex captures experiences, distills lessons, and applies them automatically. Connect multiple agents to shared memory and they improve together, overnight.
|
|
4
4
|
|
|
5
|
-
Works with **OpenClaw** (in-process plugin) and
|
|
5
|
+
Works with **Claude Code** (primary), **OpenClaw** (in-process plugin), and any MCP-compatible agent.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
**Website:** [hicortex.gamaze.com](https://hicortex.gamaze.com) · **Docs:** [hicortex.gamaze.com/docs](https://hicortex.gamaze.com/docs/)
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- LLM provider (auto-detected from OC config or `ANTHROPIC_API_KEY` for CC)
|
|
11
|
-
- ~500MB disk for database + embedding model
|
|
12
|
-
|
|
13
|
-
## Install — OpenClaw
|
|
9
|
+
## Install — Server Mode (single machine)
|
|
14
10
|
|
|
15
11
|
```bash
|
|
16
|
-
|
|
17
|
-
openclaw gateway restart
|
|
12
|
+
npx @gamaze/hicortex init
|
|
18
13
|
```
|
|
19
14
|
|
|
20
|
-
|
|
15
|
+
Detects your environment, installs a persistent MCP server daemon, auto-detects Ollama/Claude CLI/API keys, and registers with Claude Code. One command.
|
|
21
16
|
|
|
22
|
-
## Install —
|
|
17
|
+
## Install — Client Mode (multi-client)
|
|
23
18
|
|
|
24
19
|
```bash
|
|
25
|
-
npx @gamaze/hicortex init
|
|
20
|
+
npx @gamaze/hicortex init --server https://your-server.example.com
|
|
26
21
|
```
|
|
27
22
|
|
|
28
|
-
|
|
23
|
+
Connects to a remote Hicortex server. Sessions are distilled locally (privacy), memories are sent to the shared server. No local database needed.
|
|
29
24
|
|
|
30
|
-
|
|
25
|
+
## Install — OpenClaw
|
|
31
26
|
|
|
32
27
|
```bash
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Register with CC
|
|
37
|
-
claude mcp add hicortex --transport http http://localhost:8787/sse
|
|
28
|
+
openclaw plugins install @gamaze/hicortex
|
|
29
|
+
openclaw gateway restart
|
|
38
30
|
```
|
|
39
31
|
|
|
40
|
-
##
|
|
41
|
-
|
|
42
|
-
Optional config for OC (add to plugin entry in `~/.openclaw/openclaw.json`):
|
|
43
|
-
|
|
44
|
-
| Field | Default | Description |
|
|
45
|
-
|-------|---------|-------------|
|
|
46
|
-
| `licenseKey` | _(none)_ | License key. Free tier (250 memories) without key. |
|
|
47
|
-
| `llmBaseUrl` | _(auto)_ | Override LLM base URL |
|
|
48
|
-
| `llmApiKey` | _(auto)_ | Override LLM API key |
|
|
49
|
-
| `llmModel` | _(auto)_ | Override model for scoring and distillation |
|
|
50
|
-
| `reflectModel` | _(auto)_ | Override model for nightly reflection |
|
|
51
|
-
| `consolidateHour` | `2` | Hour (0-23, local time) for nightly consolidation |
|
|
52
|
-
| `dbPath` | _(auto)_ | Custom database path |
|
|
32
|
+
## Requirements
|
|
53
33
|
|
|
54
|
-
|
|
34
|
+
- Node.js 18+
|
|
35
|
+
- LLM: Ollama 9b+ (recommended), Claude CLI, or API key (Anthropic, OpenAI, etc.)
|
|
36
|
+
- ~500MB disk for database + embedding model
|
|
55
37
|
|
|
56
38
|
## What Happens Automatically
|
|
57
39
|
|
|
58
40
|
| When | What | How |
|
|
59
41
|
|------|------|-----|
|
|
60
|
-
| Agent start | Recent lessons injected into context |
|
|
61
|
-
| Agent end | Conversation captured
|
|
62
|
-
| Nightly |
|
|
42
|
+
| Agent start | Recent lessons injected into context | CLAUDE.md block / OC hook |
|
|
43
|
+
| Agent end | Conversation captured | CC: nightly transcript scan / OC: hook |
|
|
44
|
+
| Nightly | Distill → score → reflect → link → inject | Automatic pipeline |
|
|
63
45
|
|
|
64
|
-
## Agent Tools
|
|
46
|
+
## Agent Tools (MCP)
|
|
65
47
|
|
|
66
|
-
|
|
48
|
+
6 tools available via MCP:
|
|
67
49
|
|
|
68
|
-
- **hicortex_search** — Semantic search across all stored
|
|
50
|
+
- **hicortex_search** — Semantic search across all stored memories
|
|
69
51
|
- **hicortex_context** — Get recent decisions and project state
|
|
70
|
-
- **hicortex_ingest** — Store a
|
|
52
|
+
- **hicortex_ingest** — Store a memory directly
|
|
71
53
|
- **hicortex_lessons** — Get actionable lessons from reflection
|
|
54
|
+
- **hicortex_update** — Fix incorrect memories (re-embeds on content change)
|
|
55
|
+
- **hicortex_delete** — Remove memories with cascade cleanup
|
|
72
56
|
|
|
73
57
|
Skills: `/learn` to save explicit learnings.
|
|
74
58
|
|
|
75
59
|
## CLI Commands
|
|
76
60
|
|
|
77
61
|
```bash
|
|
78
|
-
npx @gamaze/hicortex server
|
|
79
|
-
npx @gamaze/hicortex init
|
|
80
|
-
npx @gamaze/hicortex
|
|
81
|
-
npx @gamaze/hicortex
|
|
82
|
-
npx @gamaze/hicortex
|
|
62
|
+
npx @gamaze/hicortex server # Start MCP server (port 8787)
|
|
63
|
+
npx @gamaze/hicortex init # Set up server mode
|
|
64
|
+
npx @gamaze/hicortex init --server <url> # Set up client mode
|
|
65
|
+
npx @gamaze/hicortex nightly # Run distill + consolidate
|
|
66
|
+
npx @gamaze/hicortex nightly --dry-run # Preview without changes
|
|
67
|
+
npx @gamaze/hicortex status # Show config, DB stats
|
|
68
|
+
npx @gamaze/hicortex uninstall # Remove CC integration (keeps DB)
|
|
83
69
|
```
|
|
84
70
|
|
|
85
71
|
## Architecture
|
|
86
72
|
|
|
87
73
|
```
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
│
|
|
91
|
-
│
|
|
92
|
-
│
|
|
93
|
-
│
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
└── Shared DB at ~/.hicortex/hicortex.db
|
|
74
|
+
Client (MBP) Server (bedrock) Client (MBA)
|
|
75
|
+
┌──────────┐ ┌──────────────┐ ┌──────────┐
|
|
76
|
+
│CC sessions│ │ Shared DB │ │CC sessions│
|
|
77
|
+
│ ↓ │ POST │ │ POST │ ↓ │
|
|
78
|
+
│ Distill │──/ingest───→│ Embed+Store │←/ingest──│ Distill │
|
|
79
|
+
│ (local) │ │ ↓ │ │ (local) │
|
|
80
|
+
│ │ MCP │ Consolidate │ MCP │ │
|
|
81
|
+
│ CC ←│──(search)───│ (score,link, │──(search)→│ CC │
|
|
82
|
+
│ │ │ reflect) │ │ │
|
|
83
|
+
└──────────┘ └──────────────┘ └──────────┘
|
|
99
84
|
|
|
100
85
|
Shared core:
|
|
101
|
-
├── SQLite + sqlite-vec + FTS5
|
|
86
|
+
├── SQLite + sqlite-vec + FTS5
|
|
102
87
|
├── bge-small-en-v1.5 embeddings (ONNX, local CPU)
|
|
103
|
-
├── BM25 + vector search with RRF fusion
|
|
104
|
-
└── Multi-provider LLM (20+ providers)
|
|
88
|
+
├── BM25 + vector search with RRF fusion + graph traversal
|
|
89
|
+
└── Multi-provider LLM (Ollama, Claude CLI, 20+ cloud providers)
|
|
105
90
|
```
|
|
106
91
|
|
|
107
|
-
##
|
|
92
|
+
## Configuration
|
|
108
93
|
|
|
109
|
-
|
|
94
|
+
Config at `~/.hicortex/config.json`. Created by `init`. Key options:
|
|
95
|
+
|
|
96
|
+
| Field | Description |
|
|
97
|
+
|-------|-------------|
|
|
98
|
+
| `mode` | `"server"` (default) or `"client"` |
|
|
99
|
+
| `serverUrl` | Remote server URL (client mode) |
|
|
100
|
+
| `llmModel` | Model for importance scoring |
|
|
101
|
+
| `distillModel` | Model for session distillation (9b+ recommended) |
|
|
102
|
+
| `reflectModel` | Model for nightly reflection (largest available) |
|
|
103
|
+
| `reflectBaseUrl` | Separate Ollama instance for reflection |
|
|
104
|
+
| `authToken` | Bearer token for endpoint auth |
|
|
105
|
+
| `licenseKey` | License key for higher tiers |
|
|
106
|
+
|
|
107
|
+
Full docs: [hicortex.gamaze.com/docs/configuration.html](https://hicortex.gamaze.com/docs/configuration.html)
|
|
108
|
+
|
|
109
|
+
## REST API
|
|
110
|
+
|
|
111
|
+
| Endpoint | Method | Auth | Description |
|
|
112
|
+
|----------|--------|------|-------------|
|
|
113
|
+
| `/health` | GET | No | Server status, memory count, version |
|
|
114
|
+
| `/ingest` | POST | Yes | Accept pre-distilled memories from clients |
|
|
115
|
+
| `/sse` | GET | Yes | MCP SSE stream for agent connections |
|
|
116
|
+
| `/messages` | POST | Yes | MCP message endpoint |
|
|
110
117
|
|
|
111
118
|
## Pricing
|
|
112
119
|
|
|
113
|
-
| Tier | Price | Memories |
|
|
114
|
-
|
|
115
|
-
| Free | $0 | 250 |
|
|
116
|
-
| Pro | $9/month | Unlimited |
|
|
117
|
-
|
|
|
118
|
-
|
|
|
120
|
+
| Tier | Price | Memories | Clients |
|
|
121
|
+
|------|-------|----------|---------|
|
|
122
|
+
| Free | $0 | 250 | Unlimited (trial) |
|
|
123
|
+
| Pro | $9/month | Unlimited | Single |
|
|
124
|
+
| Team | $29/month | Unlimited | Unlimited |
|
|
125
|
+
| Lifetime | $149 | Unlimited | Single |
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
[hicortex.gamaze.com](https://hicortex.gamaze.com)
|
|
121
128
|
|
|
122
129
|
## Uninstall
|
|
123
130
|
|
|
124
|
-
**OpenClaw:**
|
|
125
131
|
```bash
|
|
126
|
-
|
|
132
|
+
npx @gamaze/hicortex uninstall # Claude Code
|
|
133
|
+
openclaw plugins uninstall hicortex # OpenClaw
|
|
127
134
|
```
|
|
128
135
|
|
|
129
|
-
|
|
130
|
-
```bash
|
|
131
|
-
npx @gamaze/hicortex uninstall
|
|
132
|
-
```
|
|
136
|
+
Database preserved by default. Remove all data: `rm -rf ~/.hicortex`
|
|
133
137
|
|
|
134
|
-
|
|
138
|
+
## Configure — OpenClaw
|
|
139
|
+
|
|
140
|
+
Optional config (add to plugin entry in `~/.openclaw/openclaw.json`):
|
|
141
|
+
|
|
142
|
+
| Field | Default | Description |
|
|
143
|
+
|-------|---------|-------------|
|
|
144
|
+
| `licenseKey` | _(none)_ | License key. Free tier (250 memories) without key. |
|
|
145
|
+
| `llmBaseUrl` | _(auto)_ | Override LLM base URL |
|
|
146
|
+
| `llmApiKey` | _(auto)_ | Override LLM API key |
|
|
147
|
+
| `llmModel` | _(auto)_ | Override model for scoring and distillation |
|
|
148
|
+
| `reflectModel` | _(auto)_ | Override model for nightly reflection |
|
|
149
|
+
| `consolidateHour` | `2` | Hour (0-23, local time) for nightly consolidation |
|
|
150
|
+
| `dbPath` | _(auto)_ | Custom database path |
|
|
151
|
+
|
|
152
|
+
For CC, set environment variables: `ANTHROPIC_API_KEY` (auto-detected), or `HICORTEX_LLM_BASE_URL` + `HICORTEX_LLM_API_KEY` + `HICORTEX_LLM_MODEL` for custom providers.
|
|
153
|
+
|
|
154
|
+
## Database
|
|
155
|
+
|
|
156
|
+
Canonical location: `~/.hicortex/hicortex.db`. Existing OC installations at `~/.openclaw/data/hicortex.db` are automatically migrated on upgrade.
|
|
135
157
|
|
|
136
158
|
## Development
|
|
137
159
|
|
|
@@ -152,6 +174,8 @@ npm test
|
|
|
152
174
|
|
|
153
175
|
**First startup slow:** The embedding model (~130MB) downloads on first run. Allow up to 2 minutes.
|
|
154
176
|
|
|
155
|
-
**Server won't start (CC):** Check `~/.hicortex/
|
|
177
|
+
**Server won't start (CC):** Check `~/.hicortex/nightly.log` for errors. Verify port 8787 is free: `lsof -i :8787`.
|
|
156
178
|
|
|
157
179
|
**Multiple CC sessions:** The HTTP server handles multiple concurrent sessions. Do not use stdio transport — it spawns separate processes per session.
|
|
180
|
+
|
|
181
|
+
**Ollama timeout on large sessions:** Hicortex uses streaming mode with 3 retries (30s, 60s, 120s backoff). If first call fails (model loading), retry handles it automatically.
|
package/dist/cli.js
CHANGED
|
@@ -26,14 +26,17 @@ switch (command) {
|
|
|
26
26
|
});
|
|
27
27
|
break;
|
|
28
28
|
}
|
|
29
|
-
case "init":
|
|
29
|
+
case "init": {
|
|
30
|
+
const serverArg = process.argv.indexOf("--server");
|
|
31
|
+
const serverUrl = serverArg !== -1 ? process.argv[serverArg + 1] : undefined;
|
|
30
32
|
import("./init.js").then(({ runInit }) => {
|
|
31
|
-
runInit().catch((err) => {
|
|
33
|
+
runInit({ serverUrl }).catch((err) => {
|
|
32
34
|
console.error("[hicortex] Init failed:", err);
|
|
33
35
|
process.exit(1);
|
|
34
36
|
});
|
|
35
37
|
});
|
|
36
38
|
break;
|
|
39
|
+
}
|
|
37
40
|
case "nightly": {
|
|
38
41
|
const dryRun = process.argv.includes("--dry-run");
|
|
39
42
|
import("./nightly.js").then(({ runNightly }) => {
|
|
@@ -66,8 +69,9 @@ switch (command) {
|
|
|
66
69
|
Usage: hicortex <command> [options]
|
|
67
70
|
|
|
68
71
|
Commands:
|
|
69
|
-
server Start the MCP HTTP/SSE server
|
|
70
|
-
init Set up Hicortex
|
|
72
|
+
server Start the MCP HTTP/SSE server (server mode)
|
|
73
|
+
init Set up Hicortex (server mode, local DB + daemon)
|
|
74
|
+
init --server <url> Set up as client (remote server, local distillation)
|
|
71
75
|
nightly Run nightly distill + consolidate + inject
|
|
72
76
|
status Show current configuration and stats
|
|
73
77
|
uninstall Remove CC integration (preserves DB)
|
|
@@ -75,10 +79,12 @@ Commands:
|
|
|
75
79
|
Options:
|
|
76
80
|
server --port <n> Port (default: 8787)
|
|
77
81
|
server --host <h> Host (default: 127.0.0.1)
|
|
82
|
+
nightly --dry-run Preview without changes
|
|
78
83
|
|
|
79
84
|
Examples:
|
|
80
85
|
npx @gamaze/hicortex server
|
|
81
86
|
npx @gamaze/hicortex init
|
|
87
|
+
npx @gamaze/hicortex init --server https://myserver.example.com
|
|
82
88
|
npx @gamaze/hicortex status`);
|
|
83
89
|
process.exit(command ? 1 : 0);
|
|
84
90
|
}
|
package/dist/consolidate.js
CHANGED
|
@@ -228,7 +228,12 @@ async function stageReflection(db, memories, llm, budget, embedFn, dryRun) {
|
|
|
228
228
|
return `[${project}] [${agent}] ${content}`;
|
|
229
229
|
});
|
|
230
230
|
const memoriesBlock = lines.join("\n\n");
|
|
231
|
-
|
|
231
|
+
// Feed recent lessons to prevent duplicates and enable escalation
|
|
232
|
+
const recentLessons = storage.getLessons(db, 7).slice(0, 10);
|
|
233
|
+
const recentBlock = recentLessons.length > 0
|
|
234
|
+
? recentLessons.map(l => `- ${l.content.slice(0, 150)}`).join("\n")
|
|
235
|
+
: undefined;
|
|
236
|
+
const prompt = (0, prompts_js_1.reflection)(memoriesBlock, recentBlock);
|
|
232
237
|
if (dryRun) {
|
|
233
238
|
return { lessons_generated: 0, skipped: false };
|
|
234
239
|
}
|
|
@@ -250,10 +255,12 @@ async function stageReflection(db, memories, llm, budget, embedFn, dryRun) {
|
|
|
250
255
|
if (!lessonText)
|
|
251
256
|
continue;
|
|
252
257
|
const project = String(lo.project ?? "global");
|
|
258
|
+
const lessonType = String(lo.type ?? "principle");
|
|
253
259
|
const severity = String(lo.severity ?? "important");
|
|
254
260
|
const confidence = String(lo.confidence ?? "medium");
|
|
255
261
|
const sourcePattern = String(lo.source_pattern ?? "");
|
|
256
262
|
let content = `## Lesson: ${lessonText}\n\n`;
|
|
263
|
+
content += `**Type:** ${lessonType}\n`;
|
|
257
264
|
content += `**Severity:** ${severity}\n`;
|
|
258
265
|
content += `**Confidence:** ${confidence}\n`;
|
|
259
266
|
if (sourcePattern)
|
package/dist/init.d.ts
CHANGED
package/dist/init.js
CHANGED
|
@@ -316,67 +316,154 @@ async function persistLlmConfig() {
|
|
|
316
316
|
console.log(` ✓ LLM config already configured`);
|
|
317
317
|
return;
|
|
318
318
|
}
|
|
319
|
-
//
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
319
|
+
// Auto-detect all available LLM options
|
|
320
|
+
const options = [];
|
|
321
|
+
// 1. Check Ollama (local models — best for privacy and quality)
|
|
322
|
+
const ollamaModels = detectOllamaModels();
|
|
323
|
+
if (ollamaModels.length > 0) {
|
|
324
|
+
// Pick the largest model — only recommend if >= 7GB (~7b+ parameter models)
|
|
325
|
+
const best = ollamaModels[0]; // already sorted by size desc
|
|
326
|
+
const goodEnough = best.sizeGb >= 7;
|
|
327
|
+
options.push({
|
|
328
|
+
label: `Ollama ${best.name} (local${best.sizeGb ? `, ${best.sizeGb}GB` : ""}${goodEnough ? "" : ", small model"})`,
|
|
329
|
+
recommended: goodEnough,
|
|
330
|
+
save: () => {
|
|
331
|
+
config.llmBackend = "ollama";
|
|
332
|
+
config.llmBaseUrl = "http://localhost:11434";
|
|
333
|
+
config.llmModel = best.name;
|
|
334
|
+
saveConfig(configPath, config);
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
// Add other models if available
|
|
338
|
+
for (const m of ollamaModels.slice(1, 3)) {
|
|
339
|
+
options.push({
|
|
340
|
+
label: `Ollama ${m.name} (local${m.sizeGb ? `, ${m.sizeGb}GB` : ""})`,
|
|
341
|
+
save: () => {
|
|
342
|
+
config.llmBackend = "ollama";
|
|
343
|
+
config.llmBaseUrl = "http://localhost:11434";
|
|
344
|
+
config.llmModel = m.name;
|
|
345
|
+
saveConfig(configPath, config);
|
|
346
|
+
},
|
|
347
|
+
});
|
|
348
|
+
}
|
|
330
349
|
}
|
|
350
|
+
// 2. Check Claude CLI
|
|
351
|
+
const { findClaudeBinary } = await import("./llm.js");
|
|
352
|
+
const claudePath = findClaudeBinary();
|
|
353
|
+
if (claudePath) {
|
|
354
|
+
options.push({
|
|
355
|
+
label: "Claude CLI (subscription, Haiku model)",
|
|
356
|
+
recommended: ollamaModels.length === 0,
|
|
357
|
+
save: () => {
|
|
358
|
+
config.llmBackend = "claude-cli";
|
|
359
|
+
saveConfig(configPath, config);
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
// 3. Check env vars
|
|
331
364
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
365
|
+
options.push({
|
|
366
|
+
label: "Anthropic API (from ANTHROPIC_API_KEY)",
|
|
367
|
+
save: () => {
|
|
368
|
+
config.llmApiKey = process.env.ANTHROPIC_API_KEY;
|
|
369
|
+
config.llmBaseUrl = process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com";
|
|
370
|
+
config.llmProvider = "anthropic";
|
|
371
|
+
saveConfig(configPath, config);
|
|
372
|
+
},
|
|
373
|
+
});
|
|
338
374
|
}
|
|
339
|
-
|
|
375
|
+
if (process.env.OPENAI_API_KEY) {
|
|
376
|
+
options.push({
|
|
377
|
+
label: "OpenAI API (from OPENAI_API_KEY)",
|
|
378
|
+
save: () => {
|
|
379
|
+
config.llmApiKey = process.env.OPENAI_API_KEY;
|
|
380
|
+
config.llmBaseUrl = process.env.OPENAI_BASE_URL ?? "https://api.openai.com";
|
|
381
|
+
config.llmProvider = "openai";
|
|
382
|
+
saveConfig(configPath, config);
|
|
383
|
+
},
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
// 4. Check OC auth-profiles
|
|
340
387
|
const ocLlm = readOcLlmConfig();
|
|
341
388
|
if (ocLlm) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
389
|
+
options.push({
|
|
390
|
+
label: `OpenClaw (${ocLlm.provider}/${ocLlm.model ?? "default"})`,
|
|
391
|
+
save: () => {
|
|
392
|
+
config.llmApiKey = ocLlm.apiKey;
|
|
393
|
+
config.llmBaseUrl = ocLlm.baseUrl;
|
|
394
|
+
config.llmProvider = ocLlm.provider;
|
|
395
|
+
if (ocLlm.model)
|
|
396
|
+
config.llmModel = ocLlm.model;
|
|
397
|
+
saveConfig(configPath, config);
|
|
398
|
+
},
|
|
399
|
+
});
|
|
350
400
|
}
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
401
|
+
// 5. Always offer manual entry and cancel
|
|
402
|
+
options.push({
|
|
403
|
+
label: "Other provider (requires API key)",
|
|
404
|
+
save: async () => {
|
|
405
|
+
console.log("\n Providers: Anthropic, OpenAI, Google, z.ai, OpenRouter, or any OpenAI-compatible");
|
|
406
|
+
const baseUrl = await ask(" Provider base URL: ");
|
|
407
|
+
if (!baseUrl) {
|
|
408
|
+
console.log(" ⚠ Cancelled.");
|
|
409
|
+
process.exit(0);
|
|
410
|
+
}
|
|
411
|
+
const apiKey = await ask(" API key: ");
|
|
412
|
+
if (!apiKey) {
|
|
413
|
+
console.log(" ⚠ Cancelled.");
|
|
414
|
+
process.exit(0);
|
|
415
|
+
}
|
|
416
|
+
const model = await ask(" Model name (optional): ");
|
|
417
|
+
config.llmApiKey = apiKey;
|
|
418
|
+
config.llmBaseUrl = baseUrl;
|
|
419
|
+
if (model)
|
|
420
|
+
config.llmModel = model;
|
|
421
|
+
saveConfig(configPath, config);
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
options.push({
|
|
425
|
+
label: "Cancel installation",
|
|
426
|
+
save: () => {
|
|
427
|
+
console.log("\n Hicortex requires an LLM to function. Installation cancelled.");
|
|
428
|
+
process.exit(0);
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
// Find recommended index
|
|
432
|
+
const recommendedIdx = options.findIndex(o => o.recommended);
|
|
433
|
+
const defaultIdx = recommendedIdx >= 0 ? recommendedIdx : 0;
|
|
434
|
+
// Display
|
|
435
|
+
console.log("\n LLM for nightly distillation:\n");
|
|
436
|
+
for (let i = 0; i < options.length; i++) {
|
|
437
|
+
const marker = i === defaultIdx ? " (recommended)" : "";
|
|
438
|
+
console.log(` ${i + 1}. ${options[i].label}${marker}`);
|
|
439
|
+
}
|
|
440
|
+
const choice = await ask(`\n Choice [${defaultIdx + 1}]: `);
|
|
441
|
+
const selected = choice ? parseInt(choice, 10) - 1 : defaultIdx;
|
|
442
|
+
if (selected < 0 || selected >= options.length) {
|
|
443
|
+
console.log(" Invalid choice.");
|
|
444
|
+
process.exit(1);
|
|
445
|
+
}
|
|
446
|
+
await options[selected].save();
|
|
447
|
+
console.log(` ✓ LLM configured: ${options[selected].label}`);
|
|
448
|
+
}
|
|
449
|
+
function detectOllamaModels() {
|
|
450
|
+
try {
|
|
451
|
+
const resp = (0, node_child_process_1.execSync)("curl -s --max-time 2 http://localhost:11434/api/tags", {
|
|
452
|
+
encoding: "utf-8",
|
|
453
|
+
timeout: 3000,
|
|
454
|
+
});
|
|
455
|
+
const data = JSON.parse(resp);
|
|
456
|
+
const models = (data.models ?? [])
|
|
457
|
+
.filter((m) => !m.name.includes("embed")) // skip embedding models
|
|
458
|
+
.map((m) => ({
|
|
459
|
+
name: m.name,
|
|
460
|
+
sizeGb: Math.round((m.size ?? 0) / 1e9 * 10) / 10,
|
|
461
|
+
}))
|
|
462
|
+
.sort((a, b) => b.sizeGb - a.sizeGb); // largest first
|
|
463
|
+
return models;
|
|
376
464
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
process.exit(0);
|
|
465
|
+
catch {
|
|
466
|
+
return [];
|
|
380
467
|
}
|
|
381
468
|
}
|
|
382
469
|
function saveConfig(configPath, config) {
|
|
@@ -525,7 +612,11 @@ async function ask(question) {
|
|
|
525
612
|
// ---------------------------------------------------------------------------
|
|
526
613
|
// Main
|
|
527
614
|
// ---------------------------------------------------------------------------
|
|
528
|
-
async function runInit() {
|
|
615
|
+
async function runInit(options = {}) {
|
|
616
|
+
if (options.serverUrl) {
|
|
617
|
+
await runClientInit(options.serverUrl);
|
|
618
|
+
return;
|
|
619
|
+
}
|
|
529
620
|
console.log("Hicortex — Setup for Claude Code\n");
|
|
530
621
|
// Phase 1: Detect
|
|
531
622
|
console.log("Detecting existing setup...\n");
|
|
@@ -649,3 +740,232 @@ async function runInit() {
|
|
|
649
740
|
console.log(" 3. Try /learn to save something to long-term memory");
|
|
650
741
|
console.log(` 4. Check server: curl ${serverUrl}/health`);
|
|
651
742
|
}
|
|
743
|
+
// ---------------------------------------------------------------------------
|
|
744
|
+
// Client Mode Init
|
|
745
|
+
// ---------------------------------------------------------------------------
|
|
746
|
+
async function runClientInit(serverUrl) {
|
|
747
|
+
console.log("Hicortex — Client Mode Setup\n");
|
|
748
|
+
serverUrl = serverUrl.replace(/\/+$/, "");
|
|
749
|
+
// Step 1: Verify server is reachable
|
|
750
|
+
console.log(`Checking server at ${serverUrl}...`);
|
|
751
|
+
try {
|
|
752
|
+
const resp = await fetch(`${serverUrl}/health`, { signal: AbortSignal.timeout(5000) });
|
|
753
|
+
if (!resp.ok)
|
|
754
|
+
throw new Error(`HTTP ${resp.status}`);
|
|
755
|
+
const info = await resp.json();
|
|
756
|
+
console.log(` ✓ Server: v${info.version}, ${info.memories} memories, LLM: ${info.llm}`);
|
|
757
|
+
}
|
|
758
|
+
catch (err) {
|
|
759
|
+
console.error(` ✗ Cannot reach server at ${serverUrl}`);
|
|
760
|
+
console.error(` ${err instanceof Error ? err.message : String(err)}`);
|
|
761
|
+
console.error(`\n Make sure the Hicortex server is running and accessible.`);
|
|
762
|
+
process.exit(1);
|
|
763
|
+
}
|
|
764
|
+
// Step 2: Auth — try default token first, prompt only if rejected
|
|
765
|
+
const DEFAULT_AUTH_TOKEN = "hctx-default-token";
|
|
766
|
+
let authToken = DEFAULT_AUTH_TOKEN;
|
|
767
|
+
try {
|
|
768
|
+
const probe = await fetch(`${serverUrl}/ingest`, {
|
|
769
|
+
method: "POST",
|
|
770
|
+
headers: {
|
|
771
|
+
"Content-Type": "application/json",
|
|
772
|
+
"Authorization": `Bearer ${DEFAULT_AUTH_TOKEN}`,
|
|
773
|
+
},
|
|
774
|
+
body: JSON.stringify({ content: "" }),
|
|
775
|
+
signal: AbortSignal.timeout(5000),
|
|
776
|
+
});
|
|
777
|
+
if (probe.status === 401) {
|
|
778
|
+
// Server uses a custom token — ask the user
|
|
779
|
+
const tokenAnswer = await ask("\nServer uses a custom auth token. Enter token: ");
|
|
780
|
+
authToken = tokenAnswer.trim();
|
|
781
|
+
if (!authToken) {
|
|
782
|
+
console.error(" ✗ Auth token required but not provided.");
|
|
783
|
+
process.exit(1);
|
|
784
|
+
}
|
|
785
|
+
// Verify
|
|
786
|
+
const verify = await fetch(`${serverUrl}/ingest`, {
|
|
787
|
+
method: "POST",
|
|
788
|
+
headers: {
|
|
789
|
+
"Content-Type": "application/json",
|
|
790
|
+
"Authorization": `Bearer ${authToken}`,
|
|
791
|
+
},
|
|
792
|
+
body: JSON.stringify({ content: "" }),
|
|
793
|
+
signal: AbortSignal.timeout(5000),
|
|
794
|
+
});
|
|
795
|
+
if (verify.status === 401) {
|
|
796
|
+
console.error(" ✗ Auth token rejected by server.");
|
|
797
|
+
process.exit(1);
|
|
798
|
+
}
|
|
799
|
+
console.log(" ✓ Custom auth token verified");
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
console.log(" ✓ Server connected (default auth)");
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
// Probe failed but health passed — continue with default token
|
|
807
|
+
}
|
|
808
|
+
// Step 3: Configure LLM for local distillation
|
|
809
|
+
console.log("\nConfigure LLM for local session distillation:");
|
|
810
|
+
await persistLlmConfig();
|
|
811
|
+
// Step 4: Save client config
|
|
812
|
+
(0, node_fs_1.mkdirSync)(HICORTEX_HOME, { recursive: true });
|
|
813
|
+
const configPath = (0, node_path_1.join)(HICORTEX_HOME, "config.json");
|
|
814
|
+
let config = {};
|
|
815
|
+
try {
|
|
816
|
+
config = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
|
|
817
|
+
}
|
|
818
|
+
catch { }
|
|
819
|
+
config.mode = "client";
|
|
820
|
+
config.serverUrl = serverUrl;
|
|
821
|
+
if (authToken)
|
|
822
|
+
config.authToken = authToken;
|
|
823
|
+
saveConfig(configPath, config);
|
|
824
|
+
console.log(` ✓ Client config saved to ${configPath}`);
|
|
825
|
+
// Step 5: Register CC MCP pointing to remote server
|
|
826
|
+
if (authToken) {
|
|
827
|
+
// Write directly with auth header
|
|
828
|
+
const claudeJsonPath = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude.json");
|
|
829
|
+
let claudeConfig = {};
|
|
830
|
+
try {
|
|
831
|
+
claudeConfig = JSON.parse((0, node_fs_1.readFileSync)(claudeJsonPath, "utf-8"));
|
|
832
|
+
}
|
|
833
|
+
catch { }
|
|
834
|
+
if (!claudeConfig.mcpServers)
|
|
835
|
+
claudeConfig.mcpServers = {};
|
|
836
|
+
claudeConfig.mcpServers.hicortex = {
|
|
837
|
+
type: "sse",
|
|
838
|
+
url: `${serverUrl}/sse`,
|
|
839
|
+
headers: { "Authorization": `Bearer ${authToken}` },
|
|
840
|
+
};
|
|
841
|
+
(0, node_fs_1.writeFileSync)(claudeJsonPath, JSON.stringify(claudeConfig, null, 2));
|
|
842
|
+
console.log(` ✓ Registered MCP server with auth`);
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
registerCcMcp(serverUrl);
|
|
846
|
+
}
|
|
847
|
+
allowHicortexTools();
|
|
848
|
+
// Step 6: Install CC commands
|
|
849
|
+
installCcCommands();
|
|
850
|
+
// Step 7: Inject CLAUDE.md learnings block
|
|
851
|
+
const claudeMdPath = (0, node_path_1.join)((0, node_os_1.homedir)(), ".claude", "CLAUDE.md");
|
|
852
|
+
if (!(0, node_fs_1.existsSync)(claudeMdPath) || !(0, node_fs_1.readFileSync)(claudeMdPath, "utf-8").includes("HICORTEX-LEARNINGS")) {
|
|
853
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(claudeMdPath), { recursive: true });
|
|
854
|
+
let content = "";
|
|
855
|
+
try {
|
|
856
|
+
content = (0, node_fs_1.readFileSync)(claudeMdPath, "utf-8");
|
|
857
|
+
}
|
|
858
|
+
catch { }
|
|
859
|
+
const block = [
|
|
860
|
+
"<!-- HICORTEX-LEARNINGS:START -->",
|
|
861
|
+
"## Hicortex Learnings",
|
|
862
|
+
"",
|
|
863
|
+
"You have access to long-term memory via Hicortex MCP tools. Use `hicortex_search` when you need context from past sessions, decisions, or prior work. Use `hicortex_context` at session start to recall recent project state. Use `hicortex_ingest` to save important decisions or learnings. Sessions are auto-captured nightly.",
|
|
864
|
+
"<!-- HICORTEX-LEARNINGS:END -->",
|
|
865
|
+
].join("\n");
|
|
866
|
+
if (content.length > 0 && !content.endsWith("\n"))
|
|
867
|
+
content += "\n";
|
|
868
|
+
if (content.length > 0)
|
|
869
|
+
content += "\n";
|
|
870
|
+
content += block + "\n";
|
|
871
|
+
(0, node_fs_1.writeFileSync)(claudeMdPath, content);
|
|
872
|
+
console.log(` ✓ Added Hicortex Learnings block`);
|
|
873
|
+
}
|
|
874
|
+
// Step 8: Install nightly cron (distill locally, POST to server)
|
|
875
|
+
installNightlyCron();
|
|
876
|
+
console.log("\n✓ Hicortex client setup complete!\n");
|
|
877
|
+
console.log("How it works:");
|
|
878
|
+
console.log(" • MCP tools (search, context, ingest) talk to the remote server");
|
|
879
|
+
console.log(" • Nightly pipeline distills CC transcripts locally, POSTs memories to server");
|
|
880
|
+
console.log(" • No local database — all memories stored on the server");
|
|
881
|
+
console.log(`\nServer: ${serverUrl}`);
|
|
882
|
+
console.log("Restart Claude Code to activate.");
|
|
883
|
+
}
|
|
884
|
+
function installNightlyCron() {
|
|
885
|
+
const npxPath = findNpxPath();
|
|
886
|
+
const packageSpec = getPackageSpec();
|
|
887
|
+
const os = (0, node_os_1.platform)();
|
|
888
|
+
if (os === "darwin") {
|
|
889
|
+
const plistDir = (0, node_path_1.join)((0, node_os_1.homedir)(), "Library", "LaunchAgents");
|
|
890
|
+
const plistPath = (0, node_path_1.join)(plistDir, "com.gamaze.hicortex-nightly.plist");
|
|
891
|
+
const logPath = (0, node_path_1.join)(HICORTEX_HOME, "nightly.log");
|
|
892
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
893
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
894
|
+
<plist version="1.0">
|
|
895
|
+
<dict>
|
|
896
|
+
<key>Label</key>
|
|
897
|
+
<string>com.gamaze.hicortex-nightly</string>
|
|
898
|
+
<key>ProgramArguments</key>
|
|
899
|
+
<array>
|
|
900
|
+
<string>${npxPath}</string>
|
|
901
|
+
<string>-y</string>
|
|
902
|
+
<string>${packageSpec}</string>
|
|
903
|
+
<string>nightly</string>
|
|
904
|
+
</array>
|
|
905
|
+
<key>StartCalendarInterval</key>
|
|
906
|
+
<dict>
|
|
907
|
+
<key>Hour</key>
|
|
908
|
+
<integer>2</integer>
|
|
909
|
+
<key>Minute</key>
|
|
910
|
+
<integer>0</integer>
|
|
911
|
+
</dict>
|
|
912
|
+
<key>StandardOutPath</key>
|
|
913
|
+
<string>${logPath}</string>
|
|
914
|
+
<key>StandardErrorPath</key>
|
|
915
|
+
<string>${logPath}</string>
|
|
916
|
+
<key>EnvironmentVariables</key>
|
|
917
|
+
<dict>
|
|
918
|
+
<key>PATH</key>
|
|
919
|
+
<string>${(0, node_path_1.dirname)(npxPath)}:/usr/local/bin:/usr/bin:/bin</string>
|
|
920
|
+
</dict>
|
|
921
|
+
</dict>
|
|
922
|
+
</plist>`;
|
|
923
|
+
(0, node_fs_1.mkdirSync)(plistDir, { recursive: true });
|
|
924
|
+
(0, node_fs_1.writeFileSync)(plistPath, plist);
|
|
925
|
+
try {
|
|
926
|
+
try {
|
|
927
|
+
(0, node_child_process_1.execSync)(`launchctl unload ${plistPath} 2>/dev/null`, { stdio: "pipe" });
|
|
928
|
+
}
|
|
929
|
+
catch { }
|
|
930
|
+
(0, node_child_process_1.execSync)(`launchctl load ${plistPath}`, { stdio: "pipe" });
|
|
931
|
+
console.log(` ✓ Installed nightly cron (runs daily at 02:00)`);
|
|
932
|
+
}
|
|
933
|
+
catch {
|
|
934
|
+
console.log(` ⚠ Could not load nightly plist. Load manually: launchctl load ${plistPath}`);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
else if (os === "linux") {
|
|
938
|
+
const configDir = (0, node_path_1.join)((0, node_os_1.homedir)(), ".config", "systemd", "user");
|
|
939
|
+
const servicePath = (0, node_path_1.join)(configDir, "hicortex-nightly.service");
|
|
940
|
+
const timerPath = (0, node_path_1.join)(configDir, "hicortex-nightly.timer");
|
|
941
|
+
const service = `[Unit]
|
|
942
|
+
Description=Hicortex Nightly (distill + POST)
|
|
943
|
+
|
|
944
|
+
[Service]
|
|
945
|
+
Type=oneshot
|
|
946
|
+
ExecStart=${npxPath} -y ${packageSpec} nightly
|
|
947
|
+
Environment=PATH=/usr/local/bin:/usr/bin:/bin
|
|
948
|
+
Environment=HOME=${(0, node_os_1.homedir)()}
|
|
949
|
+
WorkingDirectory=${(0, node_os_1.homedir)()}`;
|
|
950
|
+
const timer = `[Unit]
|
|
951
|
+
Description=Hicortex Nightly Timer
|
|
952
|
+
|
|
953
|
+
[Timer]
|
|
954
|
+
OnCalendar=*-*-* 02:00:00
|
|
955
|
+
Persistent=true
|
|
956
|
+
|
|
957
|
+
[Install]
|
|
958
|
+
WantedBy=timers.target`;
|
|
959
|
+
(0, node_fs_1.mkdirSync)(configDir, { recursive: true });
|
|
960
|
+
(0, node_fs_1.writeFileSync)(servicePath, service);
|
|
961
|
+
(0, node_fs_1.writeFileSync)(timerPath, timer);
|
|
962
|
+
try {
|
|
963
|
+
(0, node_child_process_1.execSync)("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
964
|
+
(0, node_child_process_1.execSync)("systemctl --user enable --now hicortex-nightly.timer", { stdio: "pipe" });
|
|
965
|
+
console.log(` ✓ Installed nightly timer (runs daily at 02:00)`);
|
|
966
|
+
}
|
|
967
|
+
catch {
|
|
968
|
+
console.log(` ⚠ Could not enable nightly timer. Enable manually: systemctl --user enable --now hicortex-nightly.timer`);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
}
|
package/dist/license.js
CHANGED
package/dist/llm.d.ts
CHANGED
package/dist/llm.js
CHANGED
|
@@ -422,6 +422,28 @@ class LlmClient {
|
|
|
422
422
|
if (this.isRateLimited) {
|
|
423
423
|
throw new RateLimitError(this.rateLimitedUntil - Date.now());
|
|
424
424
|
}
|
|
425
|
+
const retryDelays = [30_000, 60_000, 120_000]; // 30s, 60s, 120s
|
|
426
|
+
let lastErr;
|
|
427
|
+
for (let attempt = 0; attempt <= retryDelays.length; attempt++) {
|
|
428
|
+
try {
|
|
429
|
+
return await this.completeOnce(model, prompt, maxTokens, timeoutMs);
|
|
430
|
+
}
|
|
431
|
+
catch (err) {
|
|
432
|
+
lastErr = err instanceof Error ? err : new Error(String(err));
|
|
433
|
+
const msg = lastErr.message;
|
|
434
|
+
if (attempt < retryDelays.length && (msg.includes("fetch failed") || msg.includes("ECONNREFUSED") || msg.includes("timeout") || msg.includes("Headers Timeout"))) {
|
|
435
|
+
const delay = retryDelays[attempt];
|
|
436
|
+
console.log(`[hicortex] LLM call failed (${msg.slice(0, 60)}), retry ${attempt + 1}/${retryDelays.length} in ${delay / 1000}s...`);
|
|
437
|
+
await new Promise(r => setTimeout(r, delay));
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
throw lastErr;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
throw lastErr;
|
|
445
|
+
}
|
|
446
|
+
async completeOnce(model, prompt, maxTokens, timeoutMs) {
|
|
425
447
|
if (this.config.provider === "claude-cli") {
|
|
426
448
|
return this.completeClaude(model, prompt, timeoutMs);
|
|
427
449
|
}
|
|
@@ -461,13 +483,15 @@ class LlmClient {
|
|
|
461
483
|
*/
|
|
462
484
|
async completeOllama(model, prompt, maxTokens, timeoutMs) {
|
|
463
485
|
const url = `${this.config.baseUrl.replace(/\/$/, "")}/api/generate`;
|
|
486
|
+
// Ollama can take minutes to process large contexts — use streaming to avoid
|
|
487
|
+
// Node.js fetch headers timeout (default ~300s kills long Ollama inferences)
|
|
464
488
|
const resp = await fetch(url, {
|
|
465
489
|
method: "POST",
|
|
466
490
|
headers: { "Content-Type": "application/json" },
|
|
467
491
|
body: JSON.stringify({
|
|
468
492
|
model,
|
|
469
493
|
prompt,
|
|
470
|
-
stream:
|
|
494
|
+
stream: true,
|
|
471
495
|
think: false,
|
|
472
496
|
options: { num_predict: maxTokens, num_ctx: 32768 },
|
|
473
497
|
}),
|
|
@@ -478,12 +502,33 @@ class LlmClient {
|
|
|
478
502
|
if (!resp.ok) {
|
|
479
503
|
const text = await resp.text().catch(() => "");
|
|
480
504
|
if (text.includes("1113") || text.includes("Insufficient balance")) {
|
|
481
|
-
this.handleRateLimit(resp);
|
|
505
|
+
this.handleRateLimit(resp);
|
|
482
506
|
}
|
|
483
507
|
throw new Error(`Ollama error ${resp.status}: ${text}`);
|
|
484
508
|
}
|
|
485
|
-
|
|
486
|
-
|
|
509
|
+
// Collect streamed response chunks
|
|
510
|
+
let result = "";
|
|
511
|
+
const reader = resp.body?.getReader();
|
|
512
|
+
if (!reader)
|
|
513
|
+
throw new Error("No response body");
|
|
514
|
+
const decoder = new TextDecoder();
|
|
515
|
+
while (true) {
|
|
516
|
+
const { done, value } = await reader.read();
|
|
517
|
+
if (done)
|
|
518
|
+
break;
|
|
519
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
520
|
+
for (const line of chunk.split("\n")) {
|
|
521
|
+
if (!line.trim())
|
|
522
|
+
continue;
|
|
523
|
+
try {
|
|
524
|
+
const data = JSON.parse(line);
|
|
525
|
+
if (data.response)
|
|
526
|
+
result += data.response;
|
|
527
|
+
}
|
|
528
|
+
catch { /* skip malformed lines */ }
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return result.trim();
|
|
487
532
|
}
|
|
488
533
|
/**
|
|
489
534
|
* Anthropic Messages API (/v1/messages). Used for Anthropic and z.ai.
|
package/dist/mcp-server.js
CHANGED
|
@@ -231,7 +231,7 @@ function createMcpServer() {
|
|
|
231
231
|
// ---------------------------------------------------------------------------
|
|
232
232
|
async function startServer(options = {}) {
|
|
233
233
|
const port = options.port ?? 8787;
|
|
234
|
-
const host = options.host ?? "
|
|
234
|
+
const host = options.host ?? "0.0.0.0";
|
|
235
235
|
// Initialize core
|
|
236
236
|
const dbPath = (0, db_js_1.resolveDbPath)(options.dbPath);
|
|
237
237
|
console.log(`[hicortex] Initializing database at ${dbPath}`);
|
|
@@ -308,10 +308,11 @@ async function startServer(options = {}) {
|
|
|
308
308
|
const stats = (0, db_js_1.getStats)(db, dbPath);
|
|
309
309
|
console.log(`[hicortex] Ready: ${stats.memories} memories, ${stats.links} links, ` +
|
|
310
310
|
`${Math.round(stats.db_size_bytes / 1024)} KB`);
|
|
311
|
-
// Auth token: from
|
|
311
|
+
// Auth token: from config, env var, or default (always-on baseline security)
|
|
312
|
+
const DEFAULT_AUTH_TOKEN = "hctx-default-token";
|
|
312
313
|
const authToken = savedConfig?.authToken
|
|
313
314
|
?? process.env.HICORTEX_AUTH_TOKEN
|
|
314
|
-
??
|
|
315
|
+
?? DEFAULT_AUTH_TOKEN;
|
|
315
316
|
// Express app
|
|
316
317
|
const app = (0, express_1.default)();
|
|
317
318
|
app.use(express_1.default.json());
|
|
@@ -360,6 +361,65 @@ async function startServer(options = {}) {
|
|
|
360
361
|
llm: `${llmConfig.provider}/${llmConfig.model}`,
|
|
361
362
|
});
|
|
362
363
|
});
|
|
364
|
+
// REST /ingest — accept pre-distilled memories from remote clients
|
|
365
|
+
app.post("/ingest", async (req, res) => {
|
|
366
|
+
if (!db) {
|
|
367
|
+
res.status(503).json({ error: "Server not initialized" });
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
// Pro license blocks remote ingest (upgrade to Team for multi-client)
|
|
371
|
+
const ip = req.ip ?? req.socket.remoteAddress ?? "";
|
|
372
|
+
const isLocal = ip === "127.0.0.1" || ip === "::1" || ip === "::ffff:127.0.0.1";
|
|
373
|
+
if (!isLocal) {
|
|
374
|
+
const features = (0, license_js_1.getFeatures)(stateDir);
|
|
375
|
+
if (features.remoteIngest === false) {
|
|
376
|
+
res.status(403).json({
|
|
377
|
+
error: "Pro license is single-machine. Upgrade to Team for multi-client remote ingestion.",
|
|
378
|
+
upgrade: "https://hicortex.gamaze.com/",
|
|
379
|
+
});
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
const { content, source_agent, project, memory_type, privacy, source_session, session_date } = req.body ?? {};
|
|
384
|
+
if (!content || typeof content !== "string") {
|
|
385
|
+
res.status(400).json({ error: "Missing or invalid 'content' field" });
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const validTypes = ["episode", "lesson", "fact", "decision"];
|
|
389
|
+
if (memory_type && !validTypes.includes(memory_type)) {
|
|
390
|
+
res.status(400).json({ error: `Invalid memory_type: ${memory_type}` });
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
// Dedup by source_session (idempotent — skip if already ingested)
|
|
394
|
+
if (source_session) {
|
|
395
|
+
const existing = db.prepare("SELECT COUNT(*) as cnt FROM memories WHERE source_session = ?").get(source_session);
|
|
396
|
+
if (existing.cnt > 0) {
|
|
397
|
+
res.status(200).json({ id: null, skipped: true, existing_count: existing.cnt });
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
// License check
|
|
402
|
+
const features = (0, license_js_1.getFeatures)(stateDir);
|
|
403
|
+
if (features.maxMemories > 0 && storage.countMemories(db) >= features.maxMemories) {
|
|
404
|
+
res.status(429).json({ error: "Memory limit reached", limit: features.maxMemories });
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
try {
|
|
408
|
+
const embedding = await (0, embedder_js_1.embed)(content);
|
|
409
|
+
const id = storage.insertMemory(db, content, embedding, {
|
|
410
|
+
sourceAgent: source_agent ?? "remote-client",
|
|
411
|
+
sourceSession: source_session ?? undefined,
|
|
412
|
+
project: project ?? undefined,
|
|
413
|
+
memoryType: memory_type ?? "episode",
|
|
414
|
+
privacy: privacy ?? "WORK",
|
|
415
|
+
createdAt: session_date ? new Date(session_date).toISOString() : undefined,
|
|
416
|
+
});
|
|
417
|
+
res.status(201).json({ id, message: "Memory ingested" });
|
|
418
|
+
}
|
|
419
|
+
catch (err) {
|
|
420
|
+
res.status(500).json({ error: "Ingestion failed", message: err instanceof Error ? err.message : String(err) });
|
|
421
|
+
}
|
|
422
|
+
});
|
|
363
423
|
// SSE endpoint — each connection gets its own McpServer + transport
|
|
364
424
|
app.get("/sse", async (req, res) => {
|
|
365
425
|
const transport = new sse_js_1.SSEServerTransport("/messages", res);
|
package/dist/nightly.js
CHANGED
|
@@ -96,8 +96,14 @@ function writeLastRun() {
|
|
|
96
96
|
}
|
|
97
97
|
async function runNightly(options = {}) {
|
|
98
98
|
const dryRun = options.dryRun ?? false;
|
|
99
|
-
const dbPath = (0, db_js_1.resolveDbPath)(options.dbPath);
|
|
100
99
|
const stateDir = options.stateDir ?? HICORTEX_HOME;
|
|
100
|
+
// Check mode: client or server
|
|
101
|
+
const savedConfig = readNightlyConfig(stateDir);
|
|
102
|
+
if (savedConfig?.mode === "client") {
|
|
103
|
+
await runClientNightly(savedConfig, dryRun);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const dbPath = (0, db_js_1.resolveDbPath)(options.dbPath);
|
|
101
107
|
console.log(`[hicortex] Nightly pipeline starting${dryRun ? " (dry run)" : ""}`);
|
|
102
108
|
console.log(`[hicortex] DB: ${dbPath}`);
|
|
103
109
|
// Init DB
|
|
@@ -116,18 +122,43 @@ async function runNightly(options = {}) {
|
|
|
116
122
|
}
|
|
117
123
|
else {
|
|
118
124
|
console.warn("[hicortex] claude-cli configured but binary not found, falling back");
|
|
119
|
-
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)(
|
|
125
|
+
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)({
|
|
126
|
+
llmBaseUrl: savedConfig?.llmBaseUrl,
|
|
127
|
+
llmApiKey: savedConfig?.llmApiKey,
|
|
128
|
+
llmModel: savedConfig?.llmModel,
|
|
129
|
+
reflectModel: savedConfig?.reflectModel,
|
|
130
|
+
});
|
|
120
131
|
}
|
|
121
132
|
}
|
|
133
|
+
else if (savedConfig?.llmBackend === "ollama") {
|
|
134
|
+
llmConfig = {
|
|
135
|
+
baseUrl: savedConfig.llmBaseUrl ?? "http://localhost:11434",
|
|
136
|
+
apiKey: "",
|
|
137
|
+
model: savedConfig.llmModel ?? "qwen3.5:4b",
|
|
138
|
+
reflectModel: savedConfig.reflectModel ?? savedConfig.llmModel ?? "qwen3.5:4b",
|
|
139
|
+
provider: "ollama",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
122
142
|
else {
|
|
123
143
|
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)({
|
|
124
144
|
llmBaseUrl: savedConfig?.llmBaseUrl,
|
|
125
145
|
llmApiKey: savedConfig?.llmApiKey,
|
|
126
146
|
llmModel: savedConfig?.llmModel,
|
|
147
|
+
reflectModel: savedConfig?.reflectModel,
|
|
127
148
|
});
|
|
128
149
|
}
|
|
150
|
+
// Apply distillModel and reflect overrides from config
|
|
151
|
+
if (savedConfig?.distillModel) {
|
|
152
|
+
llmConfig.distillModel = savedConfig.distillModel;
|
|
153
|
+
}
|
|
154
|
+
if (savedConfig?.reflectBaseUrl) {
|
|
155
|
+
llmConfig.reflectBaseUrl = savedConfig.reflectBaseUrl;
|
|
156
|
+
llmConfig.reflectApiKey = savedConfig.reflectApiKey ?? llmConfig.apiKey;
|
|
157
|
+
llmConfig.reflectProvider = savedConfig.reflectProvider ?? llmConfig.provider;
|
|
158
|
+
}
|
|
129
159
|
const llm = new llm_js_1.LlmClient(llmConfig);
|
|
130
|
-
|
|
160
|
+
const distillInfo = llmConfig.distillModel ? `, distill: ${llmConfig.distillModel}` : "";
|
|
161
|
+
console.log(`[hicortex] LLM: ${llmConfig.provider}/${llmConfig.model}${distillInfo}`);
|
|
131
162
|
// Step 1: Read new CC transcripts
|
|
132
163
|
const since = readLastRun();
|
|
133
164
|
console.log(`[hicortex] Reading CC transcripts since ${since.toISOString()}`);
|
|
@@ -206,3 +237,141 @@ async function runNightly(options = {}) {
|
|
|
206
237
|
db.close();
|
|
207
238
|
}
|
|
208
239
|
}
|
|
240
|
+
// ---------------------------------------------------------------------------
|
|
241
|
+
// Client Mode Nightly — distill locally, POST to remote server
|
|
242
|
+
// ---------------------------------------------------------------------------
|
|
243
|
+
async function runClientNightly(config, dryRun) {
|
|
244
|
+
const serverUrl = config.serverUrl.replace(/\/+$/, "");
|
|
245
|
+
const authToken = config.authToken;
|
|
246
|
+
console.log(`[hicortex] Client nightly starting${dryRun ? " (dry run)" : ""}`);
|
|
247
|
+
console.log(`[hicortex] Server: ${serverUrl}`);
|
|
248
|
+
// Verify server is reachable
|
|
249
|
+
try {
|
|
250
|
+
const resp = await fetch(`${serverUrl}/health`, { signal: AbortSignal.timeout(5000) });
|
|
251
|
+
if (!resp.ok)
|
|
252
|
+
throw new Error(`HTTP ${resp.status}`);
|
|
253
|
+
const data = await resp.json();
|
|
254
|
+
console.log(`[hicortex] Server OK: v${data.version}, ${data.memories} memories`);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
console.error(`[hicortex] Server unreachable at ${serverUrl}: ${err instanceof Error ? err.message : String(err)}`);
|
|
258
|
+
console.error(`[hicortex] Aborting. Will retry next run.`);
|
|
259
|
+
return; // Don't update last-run so we retry
|
|
260
|
+
}
|
|
261
|
+
// Init LLM for local distillation
|
|
262
|
+
let llmConfig;
|
|
263
|
+
if (config.llmBackend === "claude-cli") {
|
|
264
|
+
const claudePath = (0, llm_js_1.findClaudeBinary)();
|
|
265
|
+
if (claudePath) {
|
|
266
|
+
llmConfig = (0, llm_js_1.claudeCliConfig)(claudePath);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else if (config.llmBackend === "ollama") {
|
|
273
|
+
llmConfig = {
|
|
274
|
+
baseUrl: config.llmBaseUrl ?? "http://localhost:11434",
|
|
275
|
+
apiKey: "",
|
|
276
|
+
model: config.llmModel ?? "qwen3.5:4b",
|
|
277
|
+
reflectModel: config.reflectModel ?? config.llmModel ?? "qwen3.5:4b",
|
|
278
|
+
provider: "ollama",
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
llmConfig = (0, llm_js_1.resolveLlmConfigForCC)({
|
|
283
|
+
llmBaseUrl: config.llmBaseUrl,
|
|
284
|
+
llmApiKey: config.llmApiKey,
|
|
285
|
+
llmModel: config.llmModel,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (config.distillModel) {
|
|
289
|
+
llmConfig.distillModel = config.distillModel;
|
|
290
|
+
}
|
|
291
|
+
const llm = new llm_js_1.LlmClient(llmConfig);
|
|
292
|
+
console.log(`[hicortex] LLM: ${llmConfig.provider}/${llmConfig.distillModel ?? llmConfig.model}`);
|
|
293
|
+
// Read new CC transcripts
|
|
294
|
+
const since = readLastRun();
|
|
295
|
+
console.log(`[hicortex] Reading CC transcripts since ${since.toISOString()}`);
|
|
296
|
+
const batches = (0, transcript_reader_js_1.readCcTranscripts)(since);
|
|
297
|
+
console.log(`[hicortex] Found ${batches.length} new session(s)`);
|
|
298
|
+
if (batches.length === 0) {
|
|
299
|
+
console.log(`[hicortex] Nothing to distill.`);
|
|
300
|
+
if (!dryRun)
|
|
301
|
+
writeLastRun();
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
// Distill each session and POST to server
|
|
305
|
+
let memoriesIngested = 0;
|
|
306
|
+
let sessionsSent = 0;
|
|
307
|
+
for (const batch of batches) {
|
|
308
|
+
const transcript = (0, distiller_js_1.extractConversationText)(batch.entries);
|
|
309
|
+
if (transcript.length < 200) {
|
|
310
|
+
console.log(`[hicortex] Skip ${batch.sessionId.slice(0, 8)} (${batch.projectName}): too short`);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
console.log(`[hicortex] Distilling ${batch.sessionId.slice(0, 8)} (${batch.projectName}, ${batch.date})`);
|
|
314
|
+
if (dryRun) {
|
|
315
|
+
console.log(`[hicortex] [dry-run] Would distill ${transcript.length} chars`);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
try {
|
|
319
|
+
const entries = await (0, distiller_js_1.distillSession)(llm, transcript, batch.projectName, batch.date);
|
|
320
|
+
if (entries.length === 0) {
|
|
321
|
+
console.log(`[hicortex] → No memories extracted`);
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// POST each extracted memory to the server
|
|
325
|
+
let sessionCount = 0;
|
|
326
|
+
for (const entry of entries) {
|
|
327
|
+
const resp = await fetch(`${serverUrl}/ingest`, {
|
|
328
|
+
method: "POST",
|
|
329
|
+
headers: {
|
|
330
|
+
"Content-Type": "application/json",
|
|
331
|
+
...(authToken ? { "Authorization": `Bearer ${authToken}` } : {}),
|
|
332
|
+
},
|
|
333
|
+
body: JSON.stringify({
|
|
334
|
+
content: entry,
|
|
335
|
+
source_agent: `claude-code/${batch.projectName}`,
|
|
336
|
+
project: batch.projectName,
|
|
337
|
+
memory_type: "episode",
|
|
338
|
+
privacy: "WORK",
|
|
339
|
+
source_session: batch.sessionId,
|
|
340
|
+
session_date: batch.date,
|
|
341
|
+
}),
|
|
342
|
+
signal: AbortSignal.timeout(30_000),
|
|
343
|
+
});
|
|
344
|
+
const result = await resp.json();
|
|
345
|
+
if (resp.status === 201) {
|
|
346
|
+
sessionCount++;
|
|
347
|
+
memoriesIngested++;
|
|
348
|
+
}
|
|
349
|
+
else if (result.skipped) {
|
|
350
|
+
console.log(`[hicortex] → Already ingested (${result.existing_count} existing)`);
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
else if (resp.status === 401) {
|
|
354
|
+
console.error(`[hicortex] Auth failed. Check authToken in ~/.hicortex/config.json`);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
else if (resp.status === 429) {
|
|
358
|
+
console.log(`[hicortex] Server memory limit reached.`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
console.error(`[hicortex] Ingest failed (${resp.status}): ${result.error}`);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (sessionCount > 0) {
|
|
366
|
+
sessionsSent++;
|
|
367
|
+
console.log(`[hicortex] → ${sessionCount} memories sent to server`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
catch (err) {
|
|
371
|
+
console.error(`[hicortex] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (!dryRun)
|
|
375
|
+
writeLastRun();
|
|
376
|
+
console.log(`[hicortex] Client nightly complete: ${memoriesIngested} memories from ${sessionsSent} sessions → ${serverUrl}`);
|
|
377
|
+
}
|
package/dist/prompts.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare function importanceScoring(memoriesBlock: string): string;
|
|
|
9
9
|
/**
|
|
10
10
|
* Reflection prompt. Takes a {memories_block} with today's memories.
|
|
11
11
|
*/
|
|
12
|
-
export declare function reflection(memoriesBlock: string): string;
|
|
12
|
+
export declare function reflection(memoriesBlock: string, recentLessons?: string): string;
|
|
13
13
|
/**
|
|
14
14
|
* Distillation prompt. Extracts knowledge from a session transcript.
|
|
15
15
|
*/
|
package/dist/prompts.js
CHANGED
|
@@ -32,33 +32,61 @@ No explanations. Just the JSON array.`;
|
|
|
32
32
|
/**
|
|
33
33
|
* Reflection prompt. Takes a {memories_block} with today's memories.
|
|
34
34
|
*/
|
|
35
|
-
function reflection(memoriesBlock) {
|
|
36
|
-
|
|
35
|
+
function reflection(memoriesBlock, recentLessons) {
|
|
36
|
+
const recentSection = recentLessons
|
|
37
|
+
? `\nRECENT LESSONS (already generated — do NOT duplicate, but DO escalate if patterns recur):\n${recentLessons}\n`
|
|
38
|
+
: "";
|
|
39
|
+
return `You are a learning analyst for a multi-agent AI system. Review today's memories and extract actionable lessons from BOTH successes and failures.
|
|
37
40
|
|
|
38
|
-
|
|
41
|
+
Like human learning: we grow fastest when we reinforce what works AND correct what doesn't. A system that only learns from mistakes becomes overly cautious. A system that only learns from successes never improves. The combination multiplies.
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Good lesson: "When modifying template files processed by sed, always verify ALL substitution targets by diffing the output — partial fixes cause silent failures on other deployments"
|
|
43
|
-
Bad lesson: "The deploy script had a bug" (restatement, not actionable)
|
|
43
|
+
Quality over quantity. 1-3 lessons is typical. An empty array [] is the CORRECT response when memories show routine competent work without noteworthy patterns, surprises, or friction. Do not manufacture lessons from nothing.
|
|
44
44
|
|
|
45
|
+
LESSON TYPES:
|
|
46
|
+
- "reinforce": An approach or strategy that worked well — repeat and spread it
|
|
47
|
+
- "correct": A mistake, gap, or near-miss that should be avoided
|
|
48
|
+
- "principle": A general insight derived from either success or failure
|
|
49
|
+
${recentSection}
|
|
45
50
|
TODAY'S MEMORIES:
|
|
46
51
|
${memoriesBlock}
|
|
47
52
|
|
|
53
|
+
EXAMPLES:
|
|
54
|
+
Good reinforce: "Bundling related changes into a single PR with clear narrative gets faster approval — apply for all refactors"
|
|
55
|
+
Good reinforce: "When presenting multi-scenario analysis, show assumptions side-by-side so stakeholders evaluate trade-offs rather than reacting to isolated worst-cases"
|
|
56
|
+
Good correct: "Always verify ALL substitution targets by diffing output — partial fixes cause silent failures"
|
|
57
|
+
Good principle: "Gather evidence from logs before forming hypotheses — evidence-first debugging resolved issues 3x faster today"
|
|
58
|
+
Bad lesson: "The deploy script had a bug" (restatement, not actionable)
|
|
59
|
+
|
|
48
60
|
For each lesson, output a JSON object:
|
|
49
61
|
- "lesson": Concise, actionable rule in imperative voice
|
|
50
|
-
- "
|
|
62
|
+
- "type": "reinforce" | "correct" | "principle"
|
|
63
|
+
- "project": "global" unless genuinely project-specific (project-specific lessons are still valuable)
|
|
51
64
|
- "severity": "critical" | "important" | "minor"
|
|
52
65
|
- "confidence": "high" | "medium" | "low"
|
|
53
|
-
- "source_pattern": What triggered this (1 sentence)
|
|
66
|
+
- "source_pattern": What triggered this (1 sentence, no personal data)
|
|
54
67
|
|
|
55
|
-
|
|
56
|
-
- "
|
|
68
|
+
Severity guide:
|
|
69
|
+
- "critical": Near-misses that could have caused data loss or security breach, even if caught in time. Also: recurring patterns that keep appearing despite prior corrections.
|
|
70
|
+
- "important": Clear cause-effect, likely to recur. Worth sharing across agents.
|
|
71
|
+
- "minor": Useful optimization, single incident.
|
|
72
|
+
|
|
73
|
+
Confidence guide:
|
|
74
|
+
- "high": Pattern across multiple events, or clear cause-effect. Safe to auto-inject into agent instructions.
|
|
57
75
|
- "medium": Single incident but likely to recur. Store but don't auto-propagate.
|
|
58
76
|
- "low": Speculative. Store for retrieval only.
|
|
59
77
|
|
|
60
|
-
Focus on:
|
|
61
|
-
|
|
78
|
+
Focus on:
|
|
79
|
+
- SUCCESSES: effective strategies, approaches the user validated, patterns that saved time, clean solutions
|
|
80
|
+
- FAILURES: process gaps, repeated friction, silent failures, user corrections
|
|
81
|
+
- OMISSIONS: things that should have been done but weren't (missing tests, unchecked code paths, forgotten follow-ups)
|
|
82
|
+
- NEAR-MISSES: problems caught before damage — these deserve critical severity
|
|
83
|
+
- CONTRADICTIONS: cases where something appeared to work but didn't, or agents reached opposite conclusions
|
|
84
|
+
- CROSS-AGENT PATTERNS: same issue or success across different agents — especially high-value
|
|
85
|
+
- PROCESS FEEDBACK: user feedback about the agent's approach/behavior, not just its output
|
|
86
|
+
|
|
87
|
+
Privacy: Never include personal data (names, health, finances, credentials) in lesson text. Abstract to the process level.
|
|
88
|
+
|
|
89
|
+
Skip: isolated trivial actions, already-documented rules. However, if multiple small successes form a consistent pattern of quality, extract that pattern as a reinforcement.
|
|
62
90
|
|
|
63
91
|
Respond with a JSON array. Empty array [] is a valid response.`;
|
|
64
92
|
}
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Human-like memory for self-improving AI agents. Automatic capturing, nightly reflection, and cross-agent learning. Works with Claude Code and OpenClaw.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"node": ">=18"
|
|
36
36
|
},
|
|
37
37
|
"license": "UNLICENSED",
|
|
38
|
+
"homepage": "https://hicortex.gamaze.com",
|
|
38
39
|
"repository": {
|
|
39
40
|
"type": "git",
|
|
40
41
|
"url": "https://github.com/mha33/hicortex.git",
|