hmem-mcp 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ export function parseRequestFile(filePath, log) {
4
+ try {
5
+ const raw = fs.readFileSync(filePath, "utf-8");
6
+ const req = JSON.parse(raw);
7
+ // Extract Request_ID from filename if not in JSON
8
+ if (!req.Request_ID) {
9
+ const basename = path.basename(filePath, ".json");
10
+ // REQ_CODER_1 → CODER_1
11
+ req.Request_ID = basename.replace(/^REQ_/, "");
12
+ }
13
+ // Set defaults
14
+ req.Type = req.Type || "LAUNCH_AGENT";
15
+ req.Depth = req.Depth ?? 0;
16
+ // Terminate_After: Only set if explicitly specified in JSON.
17
+ // Template merger and agent-manager set the default later.
18
+ // An early default here would override template values (e.g. Terminate_After: false).
19
+ req._RequestFile = filePath;
20
+ return req;
21
+ }
22
+ catch (e) {
23
+ log.error(`JSON parse failed: ${filePath}: ${e}`);
24
+ return null;
25
+ }
26
+ }
27
+ const VALID_TOOLS = new Set(["opencode", "opencode-run", "gemini-cli", "claude"]);
28
+ const SAFE_ID_PATTERN = /^[A-Za-z0-9_-]+$/;
29
+ const MAX_COMMAND_LENGTH = 50_000; // 50KB max
30
+ export function validateRequest(req, maxDepth, log) {
31
+ const id = req.Agent_ID || "?";
32
+ if (req.Type === "LAUNCH_AGENT") {
33
+ if (!req.Command) {
34
+ log.error(`${id}: No command specified`);
35
+ return false;
36
+ }
37
+ if ((req.Depth ?? 0) > maxDepth) {
38
+ log.error(`${id}: Spawn-Depth ${req.Depth} > Max ${maxDepth}`);
39
+ return false;
40
+ }
41
+ }
42
+ // Agent_ID: only safe characters
43
+ if (req.Agent_ID && !SAFE_ID_PATTERN.test(req.Agent_ID)) {
44
+ log.error(`${id}: Agent_ID contains invalid characters (allowed: A-Z, 0-9, _, -)`);
45
+ return false;
46
+ }
47
+ // Tool: must be known
48
+ if (req.Tool && !VALID_TOOLS.has(req.Tool)) {
49
+ log.error(`${id}: Unknown tool '${req.Tool}' (allowed: ${[...VALID_TOOLS].join(", ")})`);
50
+ return false;
51
+ }
52
+ // Command: length limit
53
+ if (req.Command && req.Command.length > MAX_COMMAND_LENGTH) {
54
+ log.error(`${id}: Command too long (${req.Command.length} > ${MAX_COMMAND_LENGTH} bytes)`);
55
+ return false;
56
+ }
57
+ // Depth: must be non-negative
58
+ if (req.Depth !== undefined && (req.Depth < 0 || !Number.isInteger(req.Depth))) {
59
+ log.error(`${id}: Invalid depth ${req.Depth} (must be >= 0 and an integer)`);
60
+ return false;
61
+ }
62
+ return true;
63
+ }
@@ -0,0 +1,21 @@
1
+ export interface LogContext {
2
+ agent_id?: string;
3
+ pid?: number;
4
+ tool?: string;
5
+ model?: string;
6
+ [key: string]: string | number | boolean | undefined;
7
+ }
8
+ export declare class Logger {
9
+ private logFile;
10
+ private maxBytes;
11
+ private jsonMode;
12
+ private minLevel;
13
+ constructor(logDir: string, maxBytes: number, jsonMode?: boolean, minLevelStr?: string);
14
+ info(msg: string, ctx?: LogContext): void;
15
+ warn(msg: string, ctx?: LogContext): void;
16
+ error(msg: string, ctx?: LogContext): void;
17
+ action(msg: string, ctx?: LogContext): void;
18
+ debug(msg: string, ctx?: LogContext): void;
19
+ private write;
20
+ private rotate;
21
+ }
package/dist/logger.js ADDED
@@ -0,0 +1,78 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const LEVEL_PRIORITY = {
4
+ DEBUG: 0,
5
+ INFO: 1,
6
+ ACTION: 1,
7
+ WARN: 2,
8
+ ERROR: 3,
9
+ };
10
+ export class Logger {
11
+ logFile;
12
+ maxBytes;
13
+ jsonMode;
14
+ minLevel;
15
+ constructor(logDir, maxBytes, jsonMode = false, minLevelStr = "info") {
16
+ if (!fs.existsSync(logDir)) {
17
+ fs.mkdirSync(logDir, { recursive: true });
18
+ }
19
+ this.logFile = path.join(logDir, "council_log.txt");
20
+ this.maxBytes = maxBytes;
21
+ this.jsonMode = jsonMode;
22
+ this.minLevel = LEVEL_PRIORITY[minLevelStr.toUpperCase()] ?? 1;
23
+ }
24
+ info(msg, ctx) { this.write("INFO", msg, ctx); }
25
+ warn(msg, ctx) { this.write("WARN", msg, ctx); }
26
+ error(msg, ctx) { this.write("ERROR", msg, ctx); }
27
+ action(msg, ctx) { this.write("ACTION", msg, ctx); }
28
+ debug(msg, ctx) { this.write("DEBUG", msg, ctx); }
29
+ write(level, msg, ctx) {
30
+ const priority = LEVEL_PRIORITY[level] ?? 1;
31
+ if (priority < this.minLevel)
32
+ return;
33
+ let line;
34
+ if (this.jsonMode) {
35
+ const entry = {
36
+ ts: new Date().toISOString(),
37
+ level,
38
+ msg,
39
+ };
40
+ // Agent-Kontext als separate Felder
41
+ if (ctx) {
42
+ for (const [k, v] of Object.entries(ctx)) {
43
+ if (v !== undefined)
44
+ entry[k] = v;
45
+ }
46
+ }
47
+ line = JSON.stringify(entry) + "\n";
48
+ }
49
+ else {
50
+ const ts = new Date().toISOString().replace("T", " ").slice(0, 19);
51
+ const ctxStr = ctx?.agent_id ? ` [${ctx.agent_id}]` : "";
52
+ line = `[${ts}] [${level}]${ctxStr} ${msg}\n`;
53
+ }
54
+ try {
55
+ this.rotate();
56
+ fs.appendFileSync(this.logFile, line, "utf-8");
57
+ }
58
+ catch (e) {
59
+ process.stderr.write(`Logger error: ${e}\n`);
60
+ }
61
+ }
62
+ rotate() {
63
+ try {
64
+ if (!fs.existsSync(this.logFile))
65
+ return;
66
+ const stat = fs.statSync(this.logFile);
67
+ if (stat.size < this.maxBytes)
68
+ return;
69
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
70
+ const archive = this.logFile.replace(".txt", `_${ts}.txt`);
71
+ fs.renameSync(this.logFile, archive);
72
+ }
73
+ catch {
74
+ // Rotation fehlgeschlagen — weiter loggen
75
+ }
76
+ }
77
+ }
78
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * hmem — Humanlike Memory MCP Server.
4
+ *
5
+ * Provides persistent, hierarchical memory for AI agents via MCP.
6
+ * SQLite-backed, 5-level lazy loading, role-based access control.
7
+ *
8
+ * Environment variables:
9
+ * HMEM_PROJECT_DIR — Root directory where .hmem files are stored (required)
10
+ * HMEM_AGENT_ID — Agent identifier (optional; defaults to memory.hmem)
11
+ * HMEM_AGENT_ROLE — Role: worker | al | pl | ceo (default: worker)
12
+ * HMEM_AUDIT_STATE_PATH — Path to audit_state.json (default: {PROJECT_DIR}/audit_state.json)
13
+ *
14
+ * Legacy fallbacks (Das Althing):
15
+ * COUNCIL_PROJECT_DIR, COUNCIL_AGENT_ID, COUNCIL_AGENT_ROLE
16
+ */
17
+ export {};