homaruscc 0.3.0 → 0.4.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.
Files changed (45) hide show
  1. package/README.md +14 -7
  2. package/dashboard/dist/assets/index-CIzoeO8A.js +52 -0
  3. package/dashboard/dist/index.html +1 -1
  4. package/dist/claude-code-registrar.d.ts +10 -0
  5. package/dist/claude-code-registrar.d.ts.map +1 -0
  6. package/dist/claude-code-registrar.js +71 -0
  7. package/dist/claude-code-registrar.js.map +1 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +28 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/compaction-manager.d.ts +24 -0
  13. package/dist/compaction-manager.d.ts.map +1 -1
  14. package/dist/compaction-manager.js +88 -7
  15. package/dist/compaction-manager.js.map +1 -1
  16. package/dist/dashboard-server.d.ts.map +1 -1
  17. package/dist/dashboard-server.js +58 -4
  18. package/dist/dashboard-server.js.map +1 -1
  19. package/dist/mcp-tools.d.ts.map +1 -1
  20. package/dist/mcp-tools.js +28 -0
  21. package/dist/mcp-tools.js.map +1 -1
  22. package/dist/memory-index.js +1 -1
  23. package/dist/memory-index.js.map +1 -1
  24. package/dist/scaffolder.d.ts +16 -0
  25. package/dist/scaffolder.d.ts.map +1 -0
  26. package/dist/scaffolder.js +154 -0
  27. package/dist/scaffolder.js.map +1 -0
  28. package/dist/session-checkpoint.d.ts +3 -0
  29. package/dist/session-checkpoint.d.ts.map +1 -1
  30. package/dist/session-checkpoint.js +24 -0
  31. package/dist/session-checkpoint.js.map +1 -1
  32. package/dist/telegram-adapter.d.ts +3 -0
  33. package/dist/telegram-adapter.d.ts.map +1 -1
  34. package/dist/telegram-adapter.js +61 -0
  35. package/dist/telegram-adapter.js.map +1 -1
  36. package/dist/transcript-logger.d.ts +10 -0
  37. package/dist/transcript-logger.d.ts.map +1 -1
  38. package/dist/transcript-logger.js +4 -0
  39. package/dist/transcript-logger.js.map +1 -1
  40. package/dist/wizard.d.ts +24 -0
  41. package/dist/wizard.d.ts.map +1 -0
  42. package/dist/wizard.js +146 -0
  43. package/dist/wizard.js.map +1 -0
  44. package/package.json +4 -1
  45. package/dashboard/dist/assets/index-Xu4GUpcF.js +0 -49
@@ -7,7 +7,7 @@
7
7
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
8
8
  <link rel="apple-touch-icon" href="/favicon.png" />
9
9
  <title>HomarUScc Dashboard</title>
10
- <script type="module" crossorigin src="/assets/index-Xu4GUpcF.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-CIzoeO8A.js"></script>
11
11
  </head>
12
12
  <body>
13
13
  <div id="root"></div>
@@ -0,0 +1,10 @@
1
+ import type { Interface as ReadlineInterface } from "node:readline";
2
+ export declare class ClaudeCodeRegistrar {
3
+ private settingsLocations;
4
+ constructor();
5
+ detectSettingsFile(): string | null;
6
+ promptRegister(rl: ReadlineInterface): Promise<void>;
7
+ register(settingsPath: string): void;
8
+ private buildMcpEntry;
9
+ }
10
+ //# sourceMappingURL=claude-code-registrar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-registrar.d.ts","sourceRoot":"","sources":["../src/claude-code-registrar.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,IAAI,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEpE,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,iBAAiB,CAAW;;IAWpC,kBAAkB,IAAI,MAAM,GAAG,IAAI;IAQ7B,cAAc,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B1D,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAqBpC,OAAO,CAAC,aAAa;CAMtB"}
@@ -0,0 +1,71 @@
1
+ // CRC: crc-ClaudeCodeRegistrar.md | Seq: seq-first-run.md
2
+ // Detects Claude Code settings and registers HomarUScc as an MCP server.
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
4
+ import { resolve, dirname } from "node:path";
5
+ export class ClaudeCodeRegistrar {
6
+ settingsLocations;
7
+ constructor() {
8
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
9
+ this.settingsLocations = [
10
+ resolve(home, ".claude.json"),
11
+ resolve(home, ".claude", "settings.json"),
12
+ ];
13
+ }
14
+ // CRC: crc-ClaudeCodeRegistrar.md
15
+ detectSettingsFile() {
16
+ for (const path of this.settingsLocations) {
17
+ if (existsSync(path))
18
+ return path;
19
+ }
20
+ return null;
21
+ }
22
+ // CRC: crc-ClaudeCodeRegistrar.md
23
+ async promptRegister(rl) {
24
+ const settingsPath = this.detectSettingsFile();
25
+ if (!settingsPath) {
26
+ console.log("\n Claude Code settings not found. You can manually add HomarUScc later.");
27
+ console.log(" See: https://github.com/kcdjmaxx/homaruscc#claude-code-setup");
28
+ return;
29
+ }
30
+ console.log(`\n Found Claude Code settings at: ${settingsPath}`);
31
+ const answer = await new Promise((resolve) => {
32
+ rl.question(" Register HomarUScc as an MCP server? [Y/n]: ", resolve);
33
+ });
34
+ if (answer.trim().toLowerCase() === "n") {
35
+ console.log(" Skipped. You can add it manually later.");
36
+ return;
37
+ }
38
+ try {
39
+ this.register(settingsPath);
40
+ console.log(" HomarUScc registered in Claude Code settings.");
41
+ }
42
+ catch (err) {
43
+ console.log(` Could not update settings: ${String(err)}`);
44
+ console.log(" You can add it manually later.");
45
+ }
46
+ }
47
+ // CRC: crc-ClaudeCodeRegistrar.md
48
+ register(settingsPath) {
49
+ let settings = {};
50
+ if (existsSync(settingsPath)) {
51
+ const raw = readFileSync(settingsPath, "utf-8");
52
+ settings = JSON.parse(raw);
53
+ }
54
+ if (!settings.mcpServers || typeof settings.mcpServers !== "object") {
55
+ settings.mcpServers = {};
56
+ }
57
+ const mcpServers = settings.mcpServers;
58
+ mcpServers.homaruscc = this.buildMcpEntry();
59
+ // Ensure parent directory exists (for ~/.claude/settings.json case)
60
+ mkdirSync(dirname(settingsPath), { recursive: true });
61
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf-8");
62
+ }
63
+ // CRC: crc-ClaudeCodeRegistrar.md
64
+ buildMcpEntry() {
65
+ return {
66
+ command: "npx",
67
+ args: ["homaruscc"],
68
+ };
69
+ }
70
+ }
71
+ //# sourceMappingURL=claude-code-registrar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code-registrar.js","sourceRoot":"","sources":["../src/claude-code-registrar.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env node
2
+ // CRC: crc-Cli.md | Seq: seq-first-run.md, seq-normal-start.md
3
+ // CLI entry point — detects config and delegates to wizard or proxy.
4
+ import { existsSync } from "node:fs";
5
+ import { resolve } from "node:path";
6
+ // Seq: seq-first-run.md
7
+ function resolveConfigPath() {
8
+ const home = process.env.HOME ?? process.env.USERPROFILE ?? "~";
9
+ return resolve(home, ".homaruscc", "config.json");
10
+ }
11
+ async function main() {
12
+ const configPath = resolveConfigPath();
13
+ if (existsSync(configPath)) {
14
+ // Seq: seq-normal-start.md
15
+ await import("./mcp-proxy.js");
16
+ }
17
+ else {
18
+ // Seq: seq-first-run.md
19
+ const { Wizard } = await import("./wizard.js");
20
+ const wizard = new Wizard();
21
+ await wizard.run();
22
+ }
23
+ }
24
+ main().catch((err) => {
25
+ process.stderr.write(`[FATAL] [cli] ${String(err)}\n`);
26
+ process.exit(1);
27
+ });
28
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,+DAA+D;AAC/D,qEAAqE;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,wBAAwB;AACxB,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC;IAChE,OAAO,OAAO,CAAC,IAAI,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AACpD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,UAAU,GAAG,iBAAiB,EAAE,CAAC;IAEvC,IAAI,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC3B,2BAA2B;QAC3B,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,wBAAwB;QACxB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QAC5B,MAAM,MAAM,CAAC,GAAG,EAAE,CAAC;IACrB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,iBAAiB,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -1,11 +1,19 @@
1
1
  import type { Logger } from "./types.js";
2
2
  import type { HomarUScc } from "./homaruscc.js";
3
+ interface CompactionRecord {
4
+ timestamp: number;
5
+ loopRestarted: boolean;
6
+ }
3
7
  export declare class CompactionManager {
4
8
  private flushedThisCycle;
5
9
  private lastFlushTimestamp;
6
10
  private compactedSinceLastWake;
7
11
  private logger;
8
12
  private loop;
13
+ private compactionCount;
14
+ private compactionHistory;
15
+ private pendingCompaction;
16
+ private eventLoopActive;
9
17
  constructor(loop: HomarUScc, logger: Logger);
10
18
  handlePreCompact(): string;
11
19
  handlePostCompact(): string;
@@ -15,9 +23,25 @@ export declare class CompactionManager {
15
23
  * another compaction happens.
16
24
  */
17
25
  consumeCompactionFlag(): boolean;
26
+ /**
27
+ * Called when /api/wait is invoked (even without compaction flag).
28
+ * If there's a pending compaction that hasn't been consumed yet,
29
+ * this means the loop restarted via normal wake, not post-compaction wake.
30
+ */
31
+ markLoopActive(): void;
32
+ /** Called on first /api/wait — marks event loop as active for this backend lifetime */
33
+ setEventLoopActive(): void;
34
+ isEventLoopActive(): boolean;
18
35
  getFlushState(): {
19
36
  flushedThisCycle: boolean;
20
37
  lastFlushTimestamp: number;
21
38
  };
39
+ getCompactionStats(): {
40
+ count: number;
41
+ history: CompactionRecord[];
42
+ pending: CompactionRecord | null;
43
+ loopFailures: number;
44
+ };
22
45
  }
46
+ export {};
23
47
  //# sourceMappingURL=compaction-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-manager.d.ts","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;gBAEZ,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM;IAK3C,gBAAgB,IAAI,MAAM;IAsD1B,iBAAiB,IAAI,MAAM;IAuE3B;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAQhC,aAAa,IAAI;QAAE,gBAAgB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE;CAM3E"}
1
+ {"version":3,"file":"compaction-manager.d.ts","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,UAAU,gBAAgB;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,kBAAkB,CAAK;IAC/B,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IAGxB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,iBAAiB,CAAiC;IAG1D,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM;IAK3C,gBAAgB,IAAI,MAAM;IAiG1B,iBAAiB,IAAI,MAAM;IA+E3B;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAehC;;;;OAIG;IACH,cAAc,IAAI,IAAI;IAOtB,uFAAuF;IACvF,kBAAkB,IAAI,IAAI;IAO1B,iBAAiB,IAAI,OAAO;IAI5B,aAAa,IAAI;QAAE,gBAAgB,EAAE,OAAO,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE;IAO1E,kBAAkB,IAAI;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,gBAAgB,EAAE,CAAC;QAC5B,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACjC,YAAY,EAAE,MAAM,CAAC;KACtB;CAWF"}
@@ -7,6 +7,12 @@ export class CompactionManager {
7
7
  compactedSinceLastWake = false; // R150: track compaction for digest vs full delivery
8
8
  logger;
9
9
  loop;
10
+ // Compaction debug counter
11
+ compactionCount = 0;
12
+ compactionHistory = [];
13
+ pendingCompaction = null;
14
+ // Event loop tracking — set true on first /api/wait call, stays true forever
15
+ eventLoopActive = false;
10
16
  constructor(loop, logger) {
11
17
  this.loop = loop;
12
18
  this.logger = logger;
@@ -19,6 +25,10 @@ export class CompactionManager {
19
25
  this.flushedThisCycle = true;
20
26
  this.lastFlushTimestamp = Date.now();
21
27
  this.compactedSinceLastWake = true; // Set here since only PreCompact hook exists in Claude Code
28
+ // Track compaction for debugging loop failures
29
+ this.compactionCount++;
30
+ this.pendingCompaction = { timestamp: this.lastFlushTimestamp, loopRestarted: false };
31
+ this.logger.info(`Compaction #${this.compactionCount} at ${new Date(this.lastFlushTimestamp).toISOString()}`);
22
32
  this.loop.emit({
23
33
  id: randomUUID(),
24
34
  type: "pre_compact",
@@ -35,14 +45,36 @@ export class CompactionManager {
35
45
  const eventSummary = recentEvents
36
46
  .map((e) => `[${e.type}] ${e.source}: ${JSON.stringify(e.payload).slice(0, 100)}`)
37
47
  .join("\n");
38
- // R128: Save checkpoint before compaction
48
+ // R128: Save checkpoint before compaction — auto-capture texture from transcript
39
49
  const checkpoint = this.loop.getSessionCheckpoint();
50
+ const transcriptLogger = this.loop.getTranscriptLogger();
51
+ if (transcriptLogger) {
52
+ const recentTurns = transcriptLogger.getRecentTurns(8);
53
+ if (recentTurns.length > 0) {
54
+ const highlights = recentTurns.map((t) => {
55
+ const dir = t.direction === "in" ? `${t.sender ?? "user"}` : "caul";
56
+ return `[${dir}] ${t.text.slice(0, 200)}`;
57
+ });
58
+ checkpoint.update({ highlights });
59
+ }
60
+ }
40
61
  checkpoint.update({ modifiedFiles: [] }); // trigger timestamp update
41
- return [
42
- "IMPORTANT: Context compaction is about to occur. Save any important session state to memory NOW.",
43
- "Use the memory_store tool to persist anything valuable from this session that hasn't been saved yet.",
62
+ const lines = [
63
+ "IMPORTANT: Context compaction is about to occur. Save session state NOW.",
64
+ "",
65
+ "TEXTURE PRESERVATION — do these FIRST (they only take a moment):",
66
+ "",
67
+ '1. **Felt-like micro-journal**: POST to /api/checkpoint with a `texture` field — one paragraph, first person,',
68
+ " describing the *subjective quality* of this session. Not what happened, but how it felt.",
69
+ ' Example: "We were shoulder-to-shoulder in the API docs, converging from different angles."',
70
+ "",
71
+ "2. **Anchor phrases**: POST to /api/checkpoint with `anchorPhrases` — 2-3 verbatim user quotes that carried",
72
+ ' emotional or relational weight. The exact words matter more than any summary.',
73
+ ' Example: ["I want you to have this for yourself", "go for it"]',
44
74
  "",
45
- "What to save:",
75
+ "Recent exchange highlights have been auto-captured from the transcript buffer.",
76
+ "",
77
+ "Then save anything else valuable:",
46
78
  "- Decisions made during this session",
47
79
  "- Task progress and current status",
48
80
  "- Important observations or findings",
@@ -53,7 +85,14 @@ export class CompactionManager {
53
85
  "",
54
86
  "Recent events:",
55
87
  eventSummary,
56
- ].join("\n");
88
+ ];
89
+ // If the event loop was running, explicitly instruct the post-compaction instance to restart it
90
+ if (this.eventLoopActive) {
91
+ // Also save to checkpoint for belt-and-suspenders
92
+ checkpoint.update({ inProgressTask: "EVENT LOOP WAS ACTIVE — restart it after compaction: bash \"$PWD/bin/event-loop\"" });
93
+ lines.push("", "CRITICAL: The event loop (bin/event-loop) was running before compaction.", "After compaction, you MUST restart it immediately:", ' bash "$PWD/bin/event-loop"', "If you do not restart the event loop, you will stop receiving Telegram messages and timer events.");
94
+ }
95
+ return lines.join("\n");
57
96
  }
58
97
  handlePostCompact() {
59
98
  this.flushedThisCycle = false;
@@ -100,7 +139,7 @@ export class CompactionManager {
100
139
  const checkpoint = this.loop.getSessionCheckpoint();
101
140
  const checkpointText = checkpoint.toContextString();
102
141
  if (checkpointText) {
103
- lines.push("", "--- Session Checkpoint (what you were doing before compaction) ---", checkpointText);
142
+ lines.push("", "--- Session Checkpoint (what you were doing + how it felt) ---", checkpointText);
104
143
  }
105
144
  // Include active agents
106
145
  const agents = this.loop.getAgentRegistry().getAll().filter(a => a.status === "running");
@@ -110,6 +149,9 @@ export class CompactionManager {
110
149
  lines.push(` - ${a.id}: ${a.description} (started ${new Date(a.startTime).toISOString()})`);
111
150
  }
112
151
  }
152
+ if (this.eventLoopActive) {
153
+ lines.push("", "CRITICAL: The event loop was running before compaction. Restart it NOW:", ' bash "$PWD/bin/event-loop"');
154
+ }
113
155
  return lines.join("\n");
114
156
  }
115
157
  // R150: Consume-once compaction flag for digest vs full identity delivery
@@ -121,15 +163,54 @@ export class CompactionManager {
121
163
  consumeCompactionFlag() {
122
164
  if (this.compactedSinceLastWake) {
123
165
  this.compactedSinceLastWake = false;
166
+ // Mark that the loop was restarted after this compaction
167
+ if (this.pendingCompaction) {
168
+ this.pendingCompaction.loopRestarted = true;
169
+ this.compactionHistory.push(this.pendingCompaction);
170
+ this.pendingCompaction = null;
171
+ this.logger.info(`Compaction #${this.compactionCount} — loop restarted successfully`);
172
+ }
124
173
  return true;
125
174
  }
126
175
  return false;
127
176
  }
177
+ /**
178
+ * Called when /api/wait is invoked (even without compaction flag).
179
+ * If there's a pending compaction that hasn't been consumed yet,
180
+ * this means the loop restarted via normal wake, not post-compaction wake.
181
+ */
182
+ markLoopActive() {
183
+ if (this.pendingCompaction && !this.compactedSinceLastWake) {
184
+ // Edge case: compaction happened but flag was already consumed
185
+ // This shouldn't normally happen, but handle it gracefully
186
+ }
187
+ }
188
+ /** Called on first /api/wait — marks event loop as active for this backend lifetime */
189
+ setEventLoopActive() {
190
+ if (!this.eventLoopActive) {
191
+ this.eventLoopActive = true;
192
+ this.logger.info("Event loop marked active — will instruct restart after compaction");
193
+ }
194
+ }
195
+ isEventLoopActive() {
196
+ return this.eventLoopActive;
197
+ }
128
198
  getFlushState() {
129
199
  return {
130
200
  flushedThisCycle: this.flushedThisCycle,
131
201
  lastFlushTimestamp: this.lastFlushTimestamp,
132
202
  };
133
203
  }
204
+ getCompactionStats() {
205
+ // A "failure" is a compaction where loopRestarted stayed false
206
+ // (pending compaction also counts as potentially failed if old enough)
207
+ const failures = this.compactionHistory.filter(c => !c.loopRestarted).length;
208
+ return {
209
+ count: this.compactionCount,
210
+ history: [...this.compactionHistory, ...(this.pendingCompaction ? [this.pendingCompaction] : [])],
211
+ pending: this.pendingCompaction,
212
+ loopFailures: failures,
213
+ };
214
+ }
134
215
  }
135
216
  //# sourceMappingURL=compaction-manager.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"compaction-manager.js","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,sDAAsD;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIzC,MAAM,OAAO,iBAAiB;IACpB,gBAAgB,GAAG,KAAK,CAAC;IACzB,kBAAkB,GAAG,CAAC,CAAC;IACvB,sBAAsB,GAAG,KAAK,CAAC,CAAC,qDAAqD;IACrF,MAAM,CAAS;IACf,IAAI,CAAY;IAExB,YAAY,IAAe,EAAE,MAAc;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,gBAAgB;QACd,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;YACzE,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACrC,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC,4DAA4D;QAEhG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,aAAa;YACnB,MAAM,EAAE,YAAY;YACpB,SAAS,EAAE,IAAI,CAAC,kBAAkB;YAClC,OAAO,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAEhD,kDAAkD;QAClD,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CACrD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CACtF,CAAC;QAEF,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3E,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC1D,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;QAC5D,MAAM,YAAY,GAAG,YAAY;aAC9B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;aACjF,IAAI,CAAC,IAAI,CAAC,CAAC;QAEd,0CAA0C;QAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACpD,UAAU,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,2BAA2B;QAErE,OAAO;YACL,kGAAkG;YAClG,sGAAsG;YACtG,EAAE;YACF,eAAe;YACf,sCAAsC;YACtC,oCAAoC;YACpC,sCAAsC;YACtC,+CAA+C;YAC/C,EAAE;YACF,kBAAkB,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;YAC1E,WAAW,WAAW,CAAC,SAAS,WAAW,WAAW,CAAC,UAAU,iBAAiB;YAClF,EAAE;YACF,gBAAgB;YAChB,YAAY;SACb,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,iBAAiB;QACf,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC;QAC9B,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;QAEnC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;YACb,EAAE,EAAE,UAAU,EAAE;YAChB,IAAI,EAAE,cAAc;YACpB,MAAM,EAAE,YAAY;YACpB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE;SACzB,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QAEtD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;QAC5F,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,QAAQ,EAAE,CAAC;QAC1D,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACnD,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAE3D,MAAM,KAAK,GAAG;YACZ,qDAAqD;YACrD,EAAE;YACF,6BAA6B,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG;YACpG,2FAA2F;YAC3F,EAAE;SACH,CAAC;QAEF,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,kBAAkB,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,KAAK,CAAC,IAAI,CAAC,iBAAiB,WAAW,CAAC,SAAS,WAAW,WAAW,CAAC,UAAU,SAAS,CAAC,CAAC;QAE7F,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,uBAAuB,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,CAAC;QAED,IAAI,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC;YAC7C,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,sDAAsD,CAAC,CAAC;YACvE,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;gBAC7B,MAAM,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;gBAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACvD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC,CAAC;YAC5D,CAAC;QACH,CAAC;QAED,2DAA2D;QAC3D,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QACpD,MAAM,cAAc,GAAG,UAAU,CAAC,eAAe,EAAE,CAAC;QACpD,IAAI,cAAc,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,oEAAoE,EAAE,cAAc,CAAC,CAAC;QACvG,CAAC;QAED,wBAAwB;QACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QACzF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,4BAA4B,CAAC,CAAC;YAC7C,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;gBACvB,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,aAAa,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;YAC/F,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,0EAA0E;IAC1E;;;;OAIG;IACH,qBAAqB;QACnB,IAAI,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAChC,IAAI,CAAC,sBAAsB,GAAG,KAAK,CAAC;YACpC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,aAAa;QACX,OAAO;YACL,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;YACvC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;SAC5C,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"compaction-manager.js","sourceRoot":"","sources":["../src/compaction-manager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAe/D,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB;IAkBvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,gBAAgB;IAclB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAalC,OAAO,CAAC,WAAW;IAicnB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,eAAe;IAuCvB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,SAAS;CAQlB"}
1
+ {"version":3,"file":"dashboard-server.d.ts","sourceRoot":"","sources":["../src/dashboard-server.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAe/D,qBAAa,eAAe;IAC1B,OAAO,CAAC,GAAG,CAAsB;IACjC,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAe;IAC/B,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,iBAAiB,CAAoB;gBAEjC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB;IAkBvF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuB5B,OAAO,CAAC,MAAM;IAWd,OAAO,CAAC,gBAAgB;IAclB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAe3B,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IAalC,OAAO,CAAC,WAAW;IAyfnB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,eAAe;IAuCvB,OAAO,CAAC,WAAW;IAenB,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,SAAS;CAQlB"}
@@ -113,7 +113,12 @@ export class DashboardServer {
113
113
  setupRoutes() {
114
114
  // API endpoints
115
115
  this.app.get("/api/status", (_req, res) => {
116
- res.json(this.loop.getStatus());
116
+ const status = this.loop.getStatus();
117
+ const compaction = this.compactionManager.getCompactionStats();
118
+ res.json({ ...status, compaction: { count: compaction.count, loopFailures: compaction.loopFailures, pending: !!compaction.pending } });
119
+ });
120
+ this.app.get("/api/compaction-stats", (_req, res) => {
121
+ res.json(this.compactionManager.getCompactionStats());
117
122
  });
118
123
  this.app.get("/api/events", (req, res) => {
119
124
  const limit = parseInt(req.query.limit) || 50;
@@ -138,6 +143,8 @@ export class DashboardServer {
138
143
  // Maintains a server-side delivery watermark to prevent replaying old events after compaction
139
144
  // Optional `since` query param (ms timestamp) overrides the watermark
140
145
  this.app.get("/api/wait", async (req, res) => {
146
+ // Mark event loop as active on first call — persists for backend lifetime
147
+ this.compactionManager.setEventLoopActive();
141
148
  const timeout = Math.min(parseInt(req.query.timeout) || 30, 120) * 1000;
142
149
  const sinceParam = req.query.since ? parseInt(req.query.since) : undefined;
143
150
  try {
@@ -151,10 +158,12 @@ export class DashboardServer {
151
158
  const identityPayload = needsFull
152
159
  ? { soul: identity.getSoul(), user: identity.getUser(), state: identity.getAgentState(), full: true }
153
160
  : { digest: identity.getDigest(), full: false };
161
+ const compactionStats = this.compactionManager.getCompactionStats();
154
162
  res.json({
155
163
  identity: identityPayload,
156
164
  events,
157
165
  cursor: this.loop.getDeliveryWatermark(),
166
+ compaction: { count: compactionStats.count, loopFailures: compactionStats.loopFailures },
158
167
  });
159
168
  }
160
169
  }
@@ -341,9 +350,19 @@ export class DashboardServer {
341
350
  mkdirSync(dir, { recursive: true });
342
351
  writeFileSync(kanbanDataPath, JSON.stringify(data, null, 2));
343
352
  };
344
- // List all tasks
353
+ // Auto-flush done tasks older than 3 days
354
+ const flushDoneTasks = () => {
355
+ const data = readKanban();
356
+ const cutoff = Date.now() - 3 * 24 * 60 * 60 * 1000;
357
+ const before = data.tasks.length;
358
+ data.tasks = data.tasks.filter((t) => t.status !== "done" || new Date(t.updated).getTime() > cutoff);
359
+ if (data.tasks.length < before)
360
+ writeKanban(data);
361
+ return data;
362
+ };
363
+ // List all tasks (auto-flushes stale done tasks)
345
364
  this.app.get("/api/kanban/tasks", (_req, res) => {
346
- res.json(readKanban().tasks);
365
+ res.json(flushDoneTasks().tasks);
347
366
  });
348
367
  // Create a task
349
368
  this.app.post("/api/kanban/tasks", express.json(), (req, res) => {
@@ -390,7 +409,7 @@ export class DashboardServer {
390
409
  res.json({ ok: true });
391
410
  });
392
411
  // --- CRM CRUD (markdown files with YAML frontmatter) ---
393
- const crmDir = join(homedir(), ".homaruscc", "crm");
412
+ const crmDir = resolve(import.meta.dirname ?? __dirname, "..", "local", "crm");
394
413
  const parseCrmFile = (slug, content) => {
395
414
  const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
396
415
  if (!fmMatch)
@@ -552,6 +571,41 @@ export class DashboardServer {
552
571
  unlinkSync(filePath);
553
572
  res.json({ ok: true });
554
573
  });
574
+ // --- Document viewer endpoint ---
575
+ // Serves markdown files from allowed base directories
576
+ const projectDir = resolve(import.meta.dirname ?? __dirname, "..");
577
+ const halShareDir = resolve(projectDir, "../HalShare");
578
+ const homarusccDir = join(homedir(), ".homaruscc");
579
+ const allowedBases = {
580
+ "HalShare": halShareDir,
581
+ "~/.homaruscc": homarusccDir,
582
+ "crm": resolve(projectDir, "local", "crm"),
583
+ };
584
+ this.app.get("/api/docs", (req, res) => {
585
+ const filePath = req.query.path;
586
+ if (!filePath) {
587
+ res.status(400).json({ error: "path required" });
588
+ return;
589
+ }
590
+ // Resolve against allowed bases
591
+ let resolved = null;
592
+ for (const [prefix, base] of Object.entries(allowedBases)) {
593
+ if (filePath.startsWith(prefix + "/") || filePath.startsWith(prefix + "\\")) {
594
+ const relative = filePath.slice(prefix.length + 1);
595
+ const full = resolve(base, relative);
596
+ // Prevent directory traversal
597
+ if (full.startsWith(base)) {
598
+ resolved = full;
599
+ break;
600
+ }
601
+ }
602
+ }
603
+ if (!resolved || !existsSync(resolved)) {
604
+ res.status(404).json({ error: "Document not found" });
605
+ return;
606
+ }
607
+ res.type("text/markdown").send(readFileSync(resolved, "utf8"));
608
+ });
555
609
  // Serve built dashboard in production
556
610
  const distPath = resolve(import.meta.dirname ?? __dirname, "../dashboard/dist");
557
611
  if (existsSync(distPath)) {