Initial commit: Discord-Claude Gateway with event-driven agent runtime

This commit is contained in:
2026-02-22 13:44:22 -05:00
parent 68f24d50e1
commit b4f340b610
8 changed files with 335 additions and 76 deletions

View File

@@ -78,18 +78,14 @@ All settings are via environment variables:
## Markdown Config Files ## Markdown Config Files
Place these in your `CONFIG_DIR` (default: `./config/`). The gateway reads them fresh on every event — edit them anytime, no restart needed. Place these in your `CONFIG_DIR` (default: `./config/`). The gateway reads them fresh on every event — edit them anytime, no restart needed (except `agents.md` and `heartbeat.md` which are parsed at startup for cron/heartbeat timers).
| File | Purpose | Required | | File | Purpose | Required |
|------|---------|----------| |------|---------|----------|
| `identity.md` | Agent name, role, specialization | Yes | | `CLAUDE.md` | Persona: identity, personality, user context, tools — all in one | Yes |
| `soul.md` | Personality, tone, values, behavior defaults | Yes | | `agents.md` | Operating rules, cron jobs, hooks (parsed by gateway) | No |
| `agents.md` | Operating rules, safety boundaries, cron jobs, hooks | No |
| `user.md` | Info about you: name, preferences, context | No |
| `memory.md` | Long-term memory (agent can write to this) | No (auto-created) | | `memory.md` | Long-term memory (agent can write to this) | No (auto-created) |
| `tools.md` | Tool configs, API notes, usage limits | No | | `heartbeat.md` | Proactive check definitions (parsed by gateway) | No |
| `heartbeat.md` | Proactive check definitions | No |
| `boot.md` | Bootstrap configuration | No |
Missing optional files are created with default headers on first run. Missing optional files are created with default headers on first run.

View File

@@ -148,3 +148,24 @@ Our approach: Since we use `--dangerously-skip-permissions`, the agent can run B
5. Agent-managed tasks (dynamic scheduling) 5. Agent-managed tasks (dynamic scheduling)
6. Conversation archiving (audit trail) 6. Conversation archiving (audit trail)
7. Everything else as needed 7. Everything else as needed
---
## Completed: Config Simplification
Merged `soul.md`, `identity.md`, `user.md`, and `tools.md` into a single `CLAUDE.md` file. The config directory is now:
```
config/
├── CLAUDE.md ← Persona: identity, personality, user context, tools (all in one)
├── agents.md ← Cron jobs + Hooks (parsed by gateway at startup)
├── heartbeat.md ← Heartbeat checks (parsed by gateway at startup)
├── memory.md ← Long-term memory (agent-writable, auto-created)
└── sessions.json ← Channel → session ID map (auto-generated)
```
Why this split:
- `CLAUDE.md` is pure prompt context — the agent reads it but the gateway doesn't parse it
- `agents.md` and `heartbeat.md` are parsed programmatically by the gateway to set up cron timers and heartbeat intervals
- `memory.md` is the only file the agent writes to — keeping it separate prevents the agent from accidentally overwriting persona config
- Fewer files to manage, one place to edit your persona

View File

@@ -119,12 +119,11 @@ When the event reaches the front of the queue, the Agent Runtime reads ALL markd
``` ```
config/ config/
├── identity.md → Agent name, role, vibe ├── CLAUDE.md Persona: identity, personality, user context, tools
├── soul.md → Personality, tone, values ├── agents.md → Operating rules, cron jobs, hooks
├── agents.md → Operating rules, safety boundaries ├── memory.md → Long-term memory (agent-writable)
├── user.md → Info about the human ├── heartbeat.md → Proactive check definitions
── memory.md → Long-term memory (agent can write to this) ── sessions.json → Channel → session ID map (auto-generated)
└── tools.md → Tool configs, API notes
``` ```
Files are read fresh every time — edit them while the gateway is running and the next event picks up changes. Files are read fresh every time — edit them while the gateway is running and the next event picks up changes.

272
package-lock.json generated
View File

@@ -11,7 +11,9 @@
"dependencies": { "dependencies": {
"discord.js": "^14.25.1", "discord.js": "^14.25.1",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"node-cron": "^4.2.1" "node-cron": "^4.2.1",
"pino": "^10.3.1",
"pino-pretty": "^13.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^25.3.0", "@types/node": "^25.3.0",
@@ -601,6 +603,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@pinojs/redact": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": { "node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.58.0", "version": "4.58.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.58.0.tgz",
@@ -1172,6 +1180,15 @@
"node": ">=12" "node": ">=12"
} }
}, },
"node_modules/atomic-sleep": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
"integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
"license": "MIT",
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/chai": { "node_modules/chai": {
"version": "6.2.2", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
@@ -1182,6 +1199,21 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/colorette": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
"license": "MIT"
},
"node_modules/dateformat": {
"version": "4.6.3",
"resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
"integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/discord-api-types": { "node_modules/discord-api-types": {
"version": "0.38.40", "version": "0.38.40",
"resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.40.tgz", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.40.tgz",
@@ -1230,6 +1262,15 @@
"url": "https://dotenvx.com" "url": "https://dotenvx.com"
} }
}, },
"node_modules/end-of-stream": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/es-module-lexer": { "node_modules/es-module-lexer": {
"version": "1.7.0", "version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
@@ -1322,12 +1363,24 @@
"node": ">=12.17.0" "node": ">=12.17.0"
} }
}, },
"node_modules/fast-copy": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-4.0.2.tgz",
"integrity": "sha512-ybA6PDXIXOXivLJK/z9e+Otk7ve13I4ckBvGO5I2RRmBU1gMHLVDJYEuJYhGwez7YNlYji2M2DvVU+a9mSFDlw==",
"license": "MIT"
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-safe-stringify": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
"license": "MIT"
},
"node_modules/fdir": { "node_modules/fdir": {
"version": "6.5.0", "version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
@@ -1374,6 +1427,21 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
} }
}, },
"node_modules/help-me": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
"integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
"license": "MIT"
},
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.17.23", "version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
@@ -1402,6 +1470,15 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/minimist": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "3.3.11", "version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -1441,6 +1518,24 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/on-exit-leak-free": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
"integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
"license": "MIT",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/pathe": { "node_modules/pathe": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
@@ -1468,6 +1563,67 @@
"url": "https://github.com/sponsors/jonschlinkert" "url": "https://github.com/sponsors/jonschlinkert"
} }
}, },
"node_modules/pino": {
"version": "10.3.1",
"resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
"integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
"license": "MIT",
"dependencies": {
"@pinojs/redact": "^0.4.0",
"atomic-sleep": "^1.0.0",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^3.0.0",
"pino-std-serializers": "^7.0.0",
"process-warning": "^5.0.0",
"quick-format-unescaped": "^4.0.3",
"real-require": "^0.2.0",
"safe-stable-stringify": "^2.3.1",
"sonic-boom": "^4.0.1",
"thread-stream": "^4.0.0"
},
"bin": {
"pino": "bin.js"
}
},
"node_modules/pino-abstract-transport": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
"integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
"license": "MIT",
"dependencies": {
"split2": "^4.0.0"
}
},
"node_modules/pino-pretty": {
"version": "13.1.3",
"resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.3.tgz",
"integrity": "sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==",
"license": "MIT",
"dependencies": {
"colorette": "^2.0.7",
"dateformat": "^4.6.3",
"fast-copy": "^4.0.0",
"fast-safe-stringify": "^2.1.1",
"help-me": "^5.0.0",
"joycon": "^3.1.1",
"minimist": "^1.2.6",
"on-exit-leak-free": "^2.1.0",
"pino-abstract-transport": "^3.0.0",
"pump": "^3.0.0",
"secure-json-parse": "^4.0.0",
"sonic-boom": "^4.0.1",
"strip-json-comments": "^5.0.2"
},
"bin": {
"pino-pretty": "bin.js"
}
},
"node_modules/pino-std-serializers": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
"integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
"license": "MIT"
},
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.5.6", "version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
@@ -1497,6 +1653,32 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/process-warning": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
"integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "MIT"
},
"node_modules/pump": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
"integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
"license": "MIT",
"dependencies": {
"end-of-stream": "^1.1.0",
"once": "^1.3.1"
}
},
"node_modules/pure-rand": { "node_modules/pure-rand": {
"version": "7.0.1", "version": "7.0.1",
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz",
@@ -1514,6 +1696,21 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/quick-format-unescaped": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"license": "MIT"
},
"node_modules/real-require": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
"integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
"license": "MIT",
"engines": {
"node": ">= 12.13.0"
}
},
"node_modules/resolve-pkg-maps": { "node_modules/resolve-pkg-maps": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
@@ -1569,6 +1766,31 @@
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
"node_modules/safe-stable-stringify": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
"integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/secure-json-parse": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
"integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/siginfo": { "node_modules/siginfo": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
@@ -1576,6 +1798,15 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/sonic-boom": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
"integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
"license": "MIT",
"dependencies": {
"atomic-sleep": "^1.0.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -1586,6 +1817,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"license": "ISC",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/stackback": { "node_modules/stackback": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -1600,6 +1840,30 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/strip-json-comments": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz",
"integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==",
"license": "MIT",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/thread-stream": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz",
"integrity": "sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==",
"license": "MIT",
"dependencies": {
"real-require": "^0.2.0"
},
"engines": {
"node": ">=20"
}
},
"node_modules/tinybench": { "node_modules/tinybench": {
"version": "2.9.0", "version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -1875,6 +2139,12 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"node_modules/ws": { "node_modules/ws": {
"version": "8.19.0", "version": "8.19.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",

View File

@@ -15,7 +15,9 @@
"dependencies": { "dependencies": {
"discord.js": "^14.25.1", "discord.js": "^14.25.1",
"dotenv": "^17.3.1", "dotenv": "^17.3.1",
"node-cron": "^4.2.1" "node-cron": "^4.2.1",
"pino": "^10.3.1",
"pino-pretty": "^13.1.3"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^25.3.0", "@types/node": "^25.3.0",

View File

@@ -14,15 +14,13 @@ export interface BootstrapResult {
const DEFAULT_OPTIONAL_DEFAULTS: Record<string, string> = { const DEFAULT_OPTIONAL_DEFAULTS: Record<string, string> = {
"agents.md": "# Operating Rules\n", "agents.md": "# Operating Rules\n",
"user.md": "# User Context\n",
"memory.md": "# Memory\n", "memory.md": "# Memory\n",
"tools.md": "# Tool Configuration\n",
"heartbeat.md": "# Heartbeat\n", "heartbeat.md": "# Heartbeat\n",
}; };
const BUILTIN_BOOT_CONFIG: BootConfig = { const BUILTIN_BOOT_CONFIG: BootConfig = {
requiredFiles: ["soul.md", "identity.md"], requiredFiles: ["CLAUDE.md"],
optionalFiles: ["agents.md", "user.md", "memory.md", "tools.md", "heartbeat.md"], optionalFiles: ["agents.md", "memory.md", "heartbeat.md"],
defaults: { ...DEFAULT_OPTIONAL_DEFAULTS }, defaults: { ...DEFAULT_OPTIONAL_DEFAULTS },
}; };

View File

@@ -2,55 +2,37 @@ import { readFile, writeFile } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
export interface MarkdownConfigs { export interface MarkdownConfigs {
soul: string | null; /** CLAUDE.md — persona: identity, soul, user context, tools (all in one) */
identity: string | null; persona: string | null;
/** agents.md — operating rules, cron jobs, hooks (parsed by gateway) */
agents: string | null; agents: string | null;
user: string | null; /** memory.md — long-term memory (agent-writable) */
memory: string | null; memory: string | null;
tools: string | null;
} }
const CONFIG_FILES = ["soul.md", "identity.md", "agents.md", "user.md", "memory.md", "tools.md"] as const;
type ConfigKey = "soul" | "identity" | "agents" | "user" | "memory" | "tools";
const FILE_TO_KEY: Record<string, ConfigKey> = {
"soul.md": "soul",
"identity.md": "identity",
"agents.md": "agents",
"user.md": "user",
"memory.md": "memory",
"tools.md": "tools",
};
export class MarkdownConfigLoader { export class MarkdownConfigLoader {
async loadAll(configDir: string): Promise<MarkdownConfigs> { async loadAll(configDir: string): Promise<MarkdownConfigs> {
const configs: MarkdownConfigs = { const configs: MarkdownConfigs = {
soul: null, persona: null,
identity: null,
agents: null, agents: null,
user: null,
memory: null, memory: null,
tools: null,
}; };
for (const filename of CONFIG_FILES) { // CLAUDE.md — main persona file
const key = FILE_TO_KEY[filename]; configs.persona = await this.loadFile(configDir, "CLAUDE.md");
const filePath = join(configDir, filename); if (!configs.persona) {
console.warn("Warning: CLAUDE.md not found in " + configDir);
}
try { // agents.md — parsed by gateway for cron/hooks, also included in prompt
const content = await readFile(filePath, "utf-8"); configs.agents = await this.loadFile(configDir, "agents.md");
configs[key] = content;
} catch { // memory.md — agent-writable long-term memory
if (filename === "memory.md") { configs.memory = await this.loadFile(configDir, "memory.md");
const defaultContent = "# Memory\n"; if (!configs.memory) {
await writeFile(filePath, defaultContent, "utf-8"); const defaultContent = "# Memory\n";
configs[key] = defaultContent; await writeFile(join(configDir, "memory.md"), defaultContent, "utf-8");
} else { configs.memory = defaultContent;
console.warn(`Warning: ${filename} not found in ${configDir}`);
configs[key] = null;
}
}
} }
return configs; return configs;

View File

@@ -3,29 +3,20 @@ import type { MarkdownConfigs } from "./markdown-config-loader.js";
const PREAMBLE = const PREAMBLE =
"You may update your long-term memory by writing to memory.md using the Write tool. Use this to persist important facts, lessons learned, and context across sessions."; "You may update your long-term memory by writing to memory.md using the Write tool. Use this to persist important facts, lessons learned, and context across sessions.";
interface SectionDef {
key: keyof MarkdownConfigs;
header: string;
}
const SECTIONS: SectionDef[] = [
{ key: "identity", header: "## Identity" },
{ key: "soul", header: "## Personality" },
{ key: "agents", header: "## Operating Rules" },
{ key: "user", header: "## User Context" },
{ key: "memory", header: "## Long-Term Memory" },
{ key: "tools", header: "## Tool Configuration" },
];
export class SystemPromptAssembler { export class SystemPromptAssembler {
assemble(configs: MarkdownConfigs): string { assemble(configs: MarkdownConfigs): string {
const parts: string[] = [PREAMBLE, ""]; const parts: string[] = [PREAMBLE, ""];
for (const { key, header } of SECTIONS) { if (configs.persona) {
const content = configs[key]; parts.push(`## Persona\n\n${configs.persona}\n`);
if (content != null && content !== "") { }
parts.push(`${header}\n\n${content}\n`);
} if (configs.agents) {
parts.push(`## Operating Rules\n\n${configs.agents}\n`);
}
if (configs.memory) {
parts.push(`## Long-Term Memory\n\n${configs.memory}\n`);
} }
return parts.join("\n"); return parts.join("\n");