@clawdactual/chitin 0.1.4 → 0.1.6
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 +40 -24
- package/hooks/chitin/HOOK.md +27 -0
- package/hooks/chitin/handler.js +156 -0
- package/package.json +8 -1
package/README.md
CHANGED
|
@@ -96,30 +96,46 @@ chitin review # (coming soon)
|
|
|
96
96
|
chitin reflect --clear # process pending reflections
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
**Integration with OpenClaw/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
**Integration with OpenClaw / ClawdBot:**
|
|
100
|
+
|
|
101
|
+
The Chitin hook ships with the package and works with both OpenClaw and ClawdBot (legacy).
|
|
102
|
+
|
|
103
|
+
**Install from npm:**
|
|
104
|
+
```bash
|
|
105
|
+
openclaw hooks install @clawdactual/chitin
|
|
106
|
+
openclaw hooks enable chitin
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Install from local clone:**
|
|
110
|
+
```bash
|
|
111
|
+
openclaw hooks install ./
|
|
112
|
+
openclaw hooks enable chitin
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then restart your gateway. The hook handles:
|
|
116
|
+
- **agent:bootstrap** — injects PERSONALITY.md with your top insights
|
|
117
|
+
- **command:new / command:reset** — queues reflection markers for the next heartbeat
|
|
118
|
+
|
|
119
|
+
See the [hook source](hooks/chitin/) for the full implementation.
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary>Manual setup (alternative)</summary>
|
|
123
|
+
|
|
124
|
+
If you prefer manual installation, copy the hook files into your workspace:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
~/clawd/hooks/chitin/
|
|
128
|
+
├── HOOK.md # metadata + events
|
|
129
|
+
└── handler.js # bootstrap injection + reflection queuing
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Then enable hooks in your gateway config:
|
|
133
|
+
```yaml
|
|
134
|
+
hooks:
|
|
135
|
+
internal:
|
|
136
|
+
enabled: true
|
|
137
|
+
```
|
|
138
|
+
</details>
|
|
123
139
|
|
|
124
140
|
**Standalone (any agent framework):**
|
|
125
141
|
```bash
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: chitin
|
|
3
|
+
description: "Personality persistence layer — injects structured behavioral insights into sessions and queues reflection on session transitions"
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"openclaw":
|
|
7
|
+
{
|
|
8
|
+
"emoji": "🦞",
|
|
9
|
+
"events": ["agent:bootstrap", "command:new", "command:reset"],
|
|
10
|
+
},
|
|
11
|
+
"clawdbot":
|
|
12
|
+
{
|
|
13
|
+
"emoji": "🦞",
|
|
14
|
+
"events": ["agent:bootstrap", "command:new", "command:reset"],
|
|
15
|
+
},
|
|
16
|
+
}
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# Chitin Hook
|
|
20
|
+
|
|
21
|
+
## Bootstrap (agent:bootstrap)
|
|
22
|
+
Injects personality context from the Chitin insight store into every session.
|
|
23
|
+
Runs `chitin retrieve` with session context, adds output as synthetic `PERSONALITY.md`.
|
|
24
|
+
|
|
25
|
+
## Session Reset (command:new, command:reset)
|
|
26
|
+
Queues a personality reflection marker so the next heartbeat knows to
|
|
27
|
+
extract and persist new insights learned during the ended session.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chitin — Personality persistence hook for OpenClaw
|
|
3
|
+
*
|
|
4
|
+
* Two responsibilities:
|
|
5
|
+
* 1. On agent:bootstrap — inject personality context into sessions
|
|
6
|
+
* 2. On command:new/reset — trigger reflection to capture new insights
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import os from "node:os";
|
|
13
|
+
|
|
14
|
+
const DB_PATH = path.join(os.homedir(), ".config", "chitin", "insights.db");
|
|
15
|
+
const CHITIN_DIR = path.join(os.homedir(), "Personal", "chitin");
|
|
16
|
+
const CHITIN_DIST = path.join(CHITIN_DIR, "dist", "index.js");
|
|
17
|
+
const DEFAULT_BUDGET = 2000;
|
|
18
|
+
const DEBUG = !!process.env.CHITIN_DEBUG;
|
|
19
|
+
|
|
20
|
+
function log(...args) {
|
|
21
|
+
if (DEBUG) console.error("[chitin]", ...args);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ──────────────────────────────────────────────
|
|
25
|
+
// Bootstrap: inject personality context
|
|
26
|
+
// ──────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
function handleBootstrap(event) {
|
|
29
|
+
const context = event.context;
|
|
30
|
+
if (!context?.bootstrapFiles || !Array.isArray(context.bootstrapFiles)) return;
|
|
31
|
+
if (!fs.existsSync(DB_PATH)) return;
|
|
32
|
+
|
|
33
|
+
const query = buildQueryFromContext(context, event.sessionKey || "unknown");
|
|
34
|
+
if (!query) return;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const result = runChitin(
|
|
38
|
+
`retrieve --query "${escapeShell(query)}" --budget ${DEFAULT_BUDGET} --format compact`
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (!result || result.includes("No insights stored")) return;
|
|
42
|
+
|
|
43
|
+
context.bootstrapFiles.push({
|
|
44
|
+
name: "PERSONALITY.md",
|
|
45
|
+
content: [
|
|
46
|
+
"# Personality Context (Chitin)",
|
|
47
|
+
"",
|
|
48
|
+
"These are your learned behavioral patterns and insights.",
|
|
49
|
+
"They persist across sessions. You can update them with `chitin contribute`.",
|
|
50
|
+
"",
|
|
51
|
+
result,
|
|
52
|
+
].join("\n"),
|
|
53
|
+
missing: false,
|
|
54
|
+
source: "chitin-hook",
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
log(`Injected personality context (${result.length} chars) for ${event.sessionKey}`);
|
|
58
|
+
} catch (err) {
|
|
59
|
+
log(`Bootstrap error: ${err.message || err}`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ──────────────────────────────────────────────
|
|
64
|
+
// Session reset: trigger reflection
|
|
65
|
+
// ──────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
function handleSessionReset(event) {
|
|
68
|
+
// Push a reflection summary as a message that gets delivered before reset
|
|
69
|
+
// This tells the user (and any observers) that insights were captured
|
|
70
|
+
// The actual insight contribution happens via the reflection cron/heartbeat
|
|
71
|
+
// because we don't have LLM access in hooks to extract insights intelligently.
|
|
72
|
+
|
|
73
|
+
// What we CAN do: record that a session ended, so the next heartbeat
|
|
74
|
+
// knows to do a reflection pass.
|
|
75
|
+
try {
|
|
76
|
+
const markerPath = path.join(os.homedir(), ".config", "chitin", "pending-reflection.json");
|
|
77
|
+
const marker = {
|
|
78
|
+
sessionKey: event.sessionKey || "unknown",
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
reason: event.action, // "new" or "reset"
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Append to pending reflections (array)
|
|
84
|
+
let pending = [];
|
|
85
|
+
if (fs.existsSync(markerPath)) {
|
|
86
|
+
try {
|
|
87
|
+
pending = JSON.parse(fs.readFileSync(markerPath, "utf-8"));
|
|
88
|
+
if (!Array.isArray(pending)) pending = [];
|
|
89
|
+
} catch { pending = []; }
|
|
90
|
+
}
|
|
91
|
+
pending.push(marker);
|
|
92
|
+
|
|
93
|
+
// Keep only last 10
|
|
94
|
+
if (pending.length > 10) pending = pending.slice(-10);
|
|
95
|
+
|
|
96
|
+
fs.writeFileSync(markerPath, JSON.stringify(pending, null, 2));
|
|
97
|
+
log(`Queued reflection for session ${event.sessionKey} (${event.action})`);
|
|
98
|
+
} catch (err) {
|
|
99
|
+
log(`Reset handler error: ${err.message || err}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ──────────────────────────────────────────────
|
|
104
|
+
// Main handler
|
|
105
|
+
// ──────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
export default async function handler(event) {
|
|
108
|
+
if (event.type === "agent" && event.action === "bootstrap") {
|
|
109
|
+
handleBootstrap(event);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (event.type === "command" && (event.action === "new" || event.action === "reset")) {
|
|
114
|
+
handleSessionReset(event);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ──────────────────────────────────────────────
|
|
120
|
+
// Utilities
|
|
121
|
+
// ──────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
function runChitin(args) {
|
|
124
|
+
// Use compiled dist if available (much faster than npx tsx)
|
|
125
|
+
const useCompiled = fs.existsSync(CHITIN_DIST);
|
|
126
|
+
const command = useCompiled
|
|
127
|
+
? `node "${CHITIN_DIST}" --db "${DB_PATH}" ${args}`
|
|
128
|
+
: `npx tsx "${path.join(CHITIN_DIR, "src", "index.ts")}" --db "${DB_PATH}" ${args}`;
|
|
129
|
+
|
|
130
|
+
log(`Running chitin via ${useCompiled ? "compiled dist" : "tsx fallback"}`);
|
|
131
|
+
|
|
132
|
+
return execSync(command, {
|
|
133
|
+
cwd: CHITIN_DIR,
|
|
134
|
+
encoding: "utf-8",
|
|
135
|
+
timeout: useCompiled ? 3000 : 5000,
|
|
136
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
137
|
+
}).trim();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function buildQueryFromContext(context, sessionKey) {
|
|
141
|
+
const parts = [];
|
|
142
|
+
if (context.agentId) parts.push(`agent: ${context.agentId}`);
|
|
143
|
+
|
|
144
|
+
if (sessionKey.includes("telegram")) parts.push("telegram chat");
|
|
145
|
+
else if (sessionKey.includes("discord")) parts.push("discord group chat");
|
|
146
|
+
else if (sessionKey.includes("slack")) parts.push("slack work chat");
|
|
147
|
+
else if (sessionKey.includes("webchat")) parts.push("webchat direct conversation");
|
|
148
|
+
else if (sessionKey.includes("subagent")) parts.push("sub-agent task execution");
|
|
149
|
+
|
|
150
|
+
if (parts.length === 0) parts.push("general assistant session");
|
|
151
|
+
return parts.join(", ");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function escapeShell(str) {
|
|
155
|
+
return str.replace(/"/g, '\\"').replace(/\$/g, "\\$").replace(/`/g, "\\`");
|
|
156
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clawdactual/chitin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Personality persistence layer for AI agents. Structured insights about how you think, not what you remember.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,10 +9,16 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
+
"hooks",
|
|
12
13
|
"seed.json",
|
|
13
14
|
"LICENSE",
|
|
14
15
|
"README.md"
|
|
15
16
|
],
|
|
17
|
+
"openclaw": {
|
|
18
|
+
"hooks": [
|
|
19
|
+
"./hooks/chitin"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
16
22
|
"scripts": {
|
|
17
23
|
"build": "tsc",
|
|
18
24
|
"prepare": "npm run build",
|
|
@@ -29,6 +35,7 @@
|
|
|
29
35
|
"chitin",
|
|
30
36
|
"carapace",
|
|
31
37
|
"clawdbot",
|
|
38
|
+
"openclaw",
|
|
32
39
|
"mcp"
|
|
33
40
|
],
|
|
34
41
|
"author": "ClawdActual",
|