@sesamespace/hivemind 0.5.12 → 0.5.14

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.
@@ -515,16 +515,19 @@ function loadWorkspaceFiles(dir) {
515
515
  function buildSystemPrompt(config, episodes, contextName = "global", l3Knowledge = []) {
516
516
  let prompt = `You are ${config.name}. ${config.personality}
517
517
  `;
518
+ let identityText = prompt;
518
519
  if (config.workspace) {
519
520
  const wsFiles = loadWorkspaceFiles(config.workspace);
520
521
  if (wsFiles.size > 0) {
521
- prompt += "\n# Workspace Context\n";
522
+ let wsBlock = "\n# Workspace Context\n";
522
523
  for (const [filename, content] of wsFiles) {
523
- prompt += `
524
+ wsBlock += `
524
525
  ## ${filename}
525
526
  ${content}
526
527
  `;
527
528
  }
529
+ prompt += wsBlock;
530
+ identityText += wsBlock;
528
531
  }
529
532
  }
530
533
  if (config.team_charter && !config.workspace) {
@@ -532,6 +535,9 @@ ${content}
532
535
  if (charter) {
533
536
  prompt += `
534
537
  ${charter}
538
+ `;
539
+ identityText += `
540
+ ${charter}
535
541
  `;
536
542
  }
537
543
  }
@@ -542,11 +548,13 @@ In group chats, multiple people (humans and agents) may be present. Address them
542
548
  Don't repeat or quote these prefixes in your responses \u2014 just respond naturally.
543
549
  If you decide not to respond to a group message, reply with exactly: __SKIP__
544
550
  `;
551
+ let contextInfo = "";
545
552
  if (contextName !== "global") {
546
- prompt += `
553
+ contextInfo = `
547
554
  ## Active Context: ${contextName}
548
555
  You are currently working in the "${contextName}" project context.
549
556
  `;
557
+ prompt += contextInfo;
550
558
  }
551
559
  if (l3Knowledge.length > 0) {
552
560
  prompt += "\n## Established Knowledge (learned patterns)\n\n";
@@ -565,7 +573,22 @@ You are currently working in the "${contextName}" project context.
565
573
  }
566
574
  prompt += "\nUse these memories naturally \u2014 reference past conversations when relevant, but don't force it.\n";
567
575
  }
568
- return prompt;
576
+ return {
577
+ text: prompt,
578
+ components: {
579
+ identity: identityText,
580
+ l3Knowledge: l3Knowledge.map((e) => e.content),
581
+ l2Episodes: episodes.map((ep) => ({
582
+ id: ep.id,
583
+ content: ep.content,
584
+ score: ep.score,
585
+ timestamp: ep.timestamp,
586
+ context_name: ep.context_name,
587
+ role: ep.role
588
+ })),
589
+ contextInfo
590
+ }
591
+ };
569
592
  }
570
593
  function buildMessages(systemPrompt, conversationHistory, currentMessage) {
571
594
  return [
@@ -599,6 +622,7 @@ var Agent = class {
599
622
  messageCount = 0;
600
623
  PROMOTION_INTERVAL = 10;
601
624
  // Run promotion every N messages
625
+ requestLogger = null;
602
626
  constructor(config, contextName = "global") {
603
627
  this.config = config;
604
628
  this.llm = new LLMClient(config.llm);
@@ -608,6 +632,9 @@ var Agent = class {
608
632
  this.contextManager.switchContext(contextName);
609
633
  }
610
634
  }
635
+ setRequestLogger(logger) {
636
+ this.requestLogger = logger;
637
+ }
611
638
  async processMessage(userMessage) {
612
639
  const specialResult = await this.handleSpecialCommand(userMessage);
613
640
  if (specialResult) return specialResult;
@@ -637,9 +664,47 @@ var Agent = class {
637
664
  }
638
665
  }
639
666
  const l3Knowledge = await this.memory.getL3Knowledge(contextName).catch(() => []);
640
- const systemPrompt = buildSystemPrompt(this.config.agent, relevantEpisodes, contextName, l3Knowledge);
641
- const messages = buildMessages(systemPrompt, conversationHistory, userMessage);
667
+ const systemPromptResult = buildSystemPrompt(this.config.agent, relevantEpisodes, contextName, l3Knowledge);
668
+ const messages = buildMessages(systemPromptResult.text, conversationHistory, userMessage);
669
+ const llmStart = Date.now();
642
670
  const response = await this.llm.chat(messages);
671
+ const latencyMs = Date.now() - llmStart;
672
+ if (this.requestLogger) {
673
+ try {
674
+ this.requestLogger.log({
675
+ context: contextName,
676
+ contextSwitched: routing.switched,
677
+ routingReason: routing.switched ? `switched:${contextName}` : `active:${contextName}`,
678
+ rawMessage: userMessage,
679
+ systemPromptComponents: {
680
+ identity: systemPromptResult.components.identity,
681
+ l3Knowledge: systemPromptResult.components.l3Knowledge,
682
+ l2Episodes: systemPromptResult.components.l2Episodes,
683
+ contextInfo: systemPromptResult.components.contextInfo,
684
+ fullText: systemPromptResult.text
685
+ },
686
+ conversationHistory: conversationHistory.map((m) => ({
687
+ role: m.role,
688
+ content: m.content
689
+ })),
690
+ userMessage,
691
+ response: {
692
+ content: response.content,
693
+ model: response.model,
694
+ latencyMs,
695
+ skipped: response.content.trim() === "__SKIP__"
696
+ },
697
+ config: {
698
+ topK: this.config.memory.top_k,
699
+ model: this.config.llm.model,
700
+ maxTokens: this.config.llm.max_tokens,
701
+ temperature: this.config.llm.temperature
702
+ }
703
+ });
704
+ } catch (err) {
705
+ console.error("[dashboard] Failed to log request:", err.message);
706
+ }
707
+ }
643
708
  conversationHistory.push(
644
709
  { role: "user", content: userMessage },
645
710
  { role: "assistant", content: response.content }
@@ -682,7 +747,195 @@ var Agent = class {
682
747
  }
683
748
  async handleSpecialCommand(message) {
684
749
  const activeCtx = this.contextManager.getActiveContext();
685
- const searchAllMatch = message.match(/^(?:search\s+all|cross-context\s+search)[:\s]+(.+)/i);
750
+ let stripped = message.replace(/^\[.+?\s+in\s+group\s+chat\]:\s*/, "");
751
+ stripped = stripped.replace(/^\[.+?\]:\s*/, "");
752
+ const cmdText = stripped;
753
+ if (/^\/status\b/i.test(cmdText)) {
754
+ const memoryOk = await this.memory.healthCheck();
755
+ const contexts = this.contextManager.listContexts();
756
+ let contextStats = "";
757
+ try {
758
+ const daemonContexts = await this.memory.listContexts();
759
+ contextStats = daemonContexts.map((c) => ` - ${c.name}: ${c.episode_count} episodes`).join("\n");
760
+ } catch {
761
+ contextStats = " (unable to query memory daemon)";
762
+ }
763
+ const response = [
764
+ `## Agent Status`,
765
+ ``,
766
+ `**Name:** ${this.config.agent.name}`,
767
+ `**Active Context:** ${activeCtx}`,
768
+ `**Memory Daemon:** ${memoryOk ? "\u2705 connected" : "\u274C unreachable"} (${this.config.memory.daemon_url})`,
769
+ `**Model:** ${this.config.llm.model}`,
770
+ `**Temperature:** ${this.config.llm.temperature}`,
771
+ `**Max Tokens:** ${this.config.llm.max_tokens}`,
772
+ `**Memory Top-K:** ${this.config.memory.top_k}`,
773
+ `**Messages Processed:** ${this.messageCount}`,
774
+ `**L1 Contexts in Memory:** ${this.conversationHistories.size}`,
775
+ ``,
776
+ `### Contexts`,
777
+ contextStats
778
+ ].join("\n");
779
+ return { content: response, model: "system", context: activeCtx };
780
+ }
781
+ if (/^\/memory\s+stats\b/i.test(cmdText)) {
782
+ try {
783
+ const contexts = await this.memory.listContexts();
784
+ let totalEpisodes = 0;
785
+ let totalL3 = 0;
786
+ let lines = [];
787
+ for (const ctx of contexts) {
788
+ totalEpisodes += ctx.episode_count;
789
+ let l3Count = 0;
790
+ try {
791
+ const l3 = await this.memory.getL3Knowledge(ctx.name);
792
+ l3Count = l3.length;
793
+ totalL3 += l3Count;
794
+ } catch {
795
+ }
796
+ lines.push(` - **${ctx.name}**: ${ctx.episode_count} L2 episodes, ${l3Count} L3 entries`);
797
+ }
798
+ const response = [
799
+ `## Memory Stats`,
800
+ ``,
801
+ `**Total L2 Episodes:** ${totalEpisodes}`,
802
+ `**Total L3 Knowledge:** ${totalL3}`,
803
+ `**Contexts:** ${contexts.length}`,
804
+ ``,
805
+ `### Per Context`,
806
+ ...lines
807
+ ].join("\n");
808
+ return { content: response, model: "system", context: activeCtx };
809
+ } catch (err) {
810
+ return { content: `Memory stats failed: ${err.message}`, model: "system", context: activeCtx };
811
+ }
812
+ }
813
+ const memSearchMatch = cmdText.match(/^\/memory\s+search\s+(.+)/i);
814
+ if (memSearchMatch) {
815
+ const query = memSearchMatch[1].trim();
816
+ try {
817
+ const results = await this.memory.search(query, activeCtx, this.config.memory.top_k);
818
+ if (results.length === 0) {
819
+ return { content: `No memories found for "${query}" in context "${activeCtx}".`, model: "system", context: activeCtx };
820
+ }
821
+ let response = `## Memory Search: "${query}"
822
+
823
+ `;
824
+ for (const ep of results) {
825
+ const ctxLabel = ep.context_name !== activeCtx ? ` [from: ${ep.context_name}]` : "";
826
+ response += `- **[${ep.role}]** (score: ${ep.score.toFixed(3)})${ctxLabel} ${ep.content.slice(0, 300)}${ep.content.length > 300 ? "..." : ""}
827
+ `;
828
+ }
829
+ return { content: response, model: "system", context: activeCtx };
830
+ } catch (err) {
831
+ return { content: `Memory search failed: ${err.message}`, model: "system", context: activeCtx };
832
+ }
833
+ }
834
+ const l3Match = cmdText.match(/^\/memory\s+l3(?:\s+(\S+))?/i);
835
+ if (l3Match) {
836
+ const targetCtx = l3Match[1] || activeCtx;
837
+ try {
838
+ const entries = await this.memory.getL3Knowledge(targetCtx);
839
+ if (entries.length === 0) {
840
+ return { content: `No L3 knowledge in context "${targetCtx}".`, model: "system", context: activeCtx };
841
+ }
842
+ let response = `## L3 Knowledge: ${targetCtx}
843
+
844
+ `;
845
+ for (const entry of entries) {
846
+ response += `- **[${entry.id.slice(0, 8)}]** (density: ${entry.connection_density}, accesses: ${entry.access_count})
847
+ ${entry.content}
848
+ `;
849
+ }
850
+ return { content: response, model: "system", context: activeCtx };
851
+ } catch (err) {
852
+ return { content: `L3 query failed: ${err.message}`, model: "system", context: activeCtx };
853
+ }
854
+ }
855
+ if (/^\/config\b/i.test(cmdText)) {
856
+ const response = [
857
+ `## Configuration`,
858
+ ``,
859
+ `**Agent:** ${this.config.agent.name}`,
860
+ `**Personality:** ${this.config.agent.personality}`,
861
+ `**Workspace:** ${this.config.agent.workspace || "(none)"}`,
862
+ ``,
863
+ `### LLM`,
864
+ `- Model: ${this.config.llm.model}`,
865
+ `- Base URL: ${this.config.llm.base_url}`,
866
+ `- Max Tokens: ${this.config.llm.max_tokens}`,
867
+ `- Temperature: ${this.config.llm.temperature}`,
868
+ ``,
869
+ `### Memory`,
870
+ `- Daemon URL: ${this.config.memory.daemon_url}`,
871
+ `- Top-K: ${this.config.memory.top_k}`,
872
+ `- Embedding Model: ${this.config.memory.embedding_model}`,
873
+ ``,
874
+ `### Sesame`,
875
+ `- API: ${this.config.sesame.api_url}`,
876
+ `- WebSocket: ${this.config.sesame.ws_url}`,
877
+ `- API Key: ${this.config.sesame.api_key ? "***" + this.config.sesame.api_key.slice(-4) : "(not set)"}`
878
+ ].join("\n");
879
+ return { content: response, model: "system", context: activeCtx };
880
+ }
881
+ if (/^\/contexts\b/i.test(cmdText)) {
882
+ const localContexts = this.contextManager.listContexts();
883
+ let daemonInfo = "";
884
+ try {
885
+ const daemonContexts = await this.memory.listContexts();
886
+ daemonInfo = "\n### Memory Daemon Contexts\n" + daemonContexts.map(
887
+ (c) => ` - **${c.name}**: ${c.episode_count} episodes (created: ${c.created_at})`
888
+ ).join("\n");
889
+ } catch {
890
+ daemonInfo = "\n(Memory daemon unreachable)";
891
+ }
892
+ const localInfo = localContexts.map((c) => {
893
+ const active = c.name === activeCtx ? " \u2190 active" : "";
894
+ const historyLen = this.conversationHistories.get(c.name)?.length || 0;
895
+ return ` - **${c.name}**${active}: ${historyLen / 2} turns in L1`;
896
+ }).join("\n");
897
+ const response = [
898
+ `## Contexts`,
899
+ ``,
900
+ `**Active:** ${activeCtx}`,
901
+ ``,
902
+ `### Local (L1 Working Memory)`,
903
+ localInfo,
904
+ daemonInfo
905
+ ].join("\n");
906
+ return { content: response, model: "system", context: activeCtx };
907
+ }
908
+ if (/^\/help\b/i.test(cmdText)) {
909
+ const response = [
910
+ `## Available Commands`,
911
+ ``,
912
+ `### Introspection`,
913
+ `- \`/status\` \u2014 Agent health, memory status, config summary`,
914
+ `- \`/config\` \u2014 Full configuration details`,
915
+ `- \`/contexts\` \u2014 List all contexts with L1/L2 info`,
916
+ `- \`/memory stats\` \u2014 Episode counts, L3 entries per context`,
917
+ `- \`/memory search <query>\` \u2014 Search L2 memories with scores`,
918
+ `- \`/memory l3 [context]\` \u2014 Show L3 knowledge entries`,
919
+ ``,
920
+ `### Context Management`,
921
+ `- \`switch to <name>\` \u2014 Switch active context`,
922
+ `- \`create context <name>\` \u2014 Create a new context`,
923
+ `- \`list contexts\` \u2014 List contexts (legacy)`,
924
+ ``,
925
+ `### Memory`,
926
+ `- \`search all: <query>\` \u2014 Cross-context search`,
927
+ `- \`share <episode_id> with <context>\` \u2014 Share episode across contexts`,
928
+ ``,
929
+ `### Tasks`,
930
+ `- \`task add <title>\` \u2014 Create a task`,
931
+ `- \`task list [status]\` \u2014 List tasks`,
932
+ `- \`task start <id>\` \u2014 Start a task`,
933
+ `- \`task complete <id>\` \u2014 Complete a task`,
934
+ `- \`task next\` \u2014 Pick up next available task`
935
+ ].join("\n");
936
+ return { content: response, model: "system", context: activeCtx };
937
+ }
938
+ const searchAllMatch = cmdText.match(/^(?:search\s+all|cross-context\s+search)[:\s]+(.+)/i);
686
939
  if (searchAllMatch) {
687
940
  const query = searchAllMatch[1].trim();
688
941
  try {
@@ -706,7 +959,7 @@ var Agent = class {
706
959
  return { content: `Cross-context search failed: ${err.message}`, model: "system", context: activeCtx };
707
960
  }
708
961
  }
709
- const shareMatch = message.match(/^share\s+(\S+)\s+with\s+(\S+)/i);
962
+ const shareMatch = cmdText.match(/^share\s+(\S+)\s+with\s+(\S+)/i);
710
963
  if (shareMatch) {
711
964
  const episodeId = shareMatch[1];
712
965
  const targetContext = shareMatch[2];
@@ -717,7 +970,7 @@ var Agent = class {
717
970
  return { content: `Failed to share: ${err.message}`, model: "system", context: activeCtx };
718
971
  }
719
972
  }
720
- const taskCmd = TaskEngine.parseTaskCommand(message);
973
+ const taskCmd = TaskEngine.parseTaskCommand(cmdText);
721
974
  if (taskCmd) {
722
975
  const engine = new TaskEngine({ contextName: activeCtx, memory: this.memory });
723
976
  try {
@@ -1574,8 +1827,8 @@ import { resolve as resolve3, dirname as dirname2 } from "path";
1574
1827
  import { fileURLToPath } from "url";
1575
1828
  var RUNTIME_VERSION = "unknown";
1576
1829
  try {
1577
- const __dirname = dirname2(fileURLToPath(import.meta.url));
1578
- const pkg = JSON.parse(readFileSync3(resolve3(__dirname, "../package.json"), "utf-8"));
1830
+ const __dirname2 = dirname2(fileURLToPath(import.meta.url));
1831
+ const pkg = JSON.parse(readFileSync3(resolve3(__dirname2, "../package.json"), "utf-8"));
1579
1832
  RUNTIME_VERSION = pkg.version ?? "unknown";
1580
1833
  } catch {
1581
1834
  }
@@ -1737,20 +1990,285 @@ var SesameClient2 = class {
1737
1990
  };
1738
1991
 
1739
1992
  // packages/runtime/src/pipeline.ts
1740
- import { createServer } from "http";
1993
+ import { createServer as createServer2 } from "http";
1741
1994
 
1742
1995
  // packages/runtime/src/health.ts
1743
1996
  var HEALTH_PORT = 9484;
1744
1997
  var HEALTH_PATH = "/health";
1745
1998
 
1746
- // packages/runtime/src/pipeline.ts
1747
- import { readFileSync as readFileSync4, writeFileSync, unlinkSync } from "fs";
1748
- import { resolve as resolve4, dirname as dirname3 } from "path";
1999
+ // packages/runtime/src/request-logger.ts
2000
+ import Database from "better-sqlite3";
2001
+ import { randomUUID } from "crypto";
2002
+ import { mkdirSync, existsSync as existsSync3 } from "fs";
2003
+ import { dirname as dirname3 } from "path";
2004
+ var RequestLogger = class {
2005
+ db;
2006
+ constructor(dbPath) {
2007
+ const dir = dirname3(dbPath);
2008
+ if (!existsSync3(dir)) mkdirSync(dir, { recursive: true });
2009
+ this.db = new Database(dbPath);
2010
+ this.db.pragma("journal_mode = WAL");
2011
+ this.db.pragma("synchronous = NORMAL");
2012
+ this.init();
2013
+ this.prune();
2014
+ }
2015
+ init() {
2016
+ this.db.exec(`
2017
+ CREATE TABLE IF NOT EXISTS request_logs (
2018
+ id TEXT PRIMARY KEY,
2019
+ timestamp TEXT NOT NULL,
2020
+ context TEXT NOT NULL,
2021
+ context_switched INTEGER NOT NULL DEFAULT 0,
2022
+ routing_reason TEXT NOT NULL DEFAULT '',
2023
+ channel_id TEXT NOT NULL DEFAULT '',
2024
+ channel_kind TEXT NOT NULL DEFAULT '',
2025
+ sender_handle TEXT NOT NULL DEFAULT '',
2026
+ raw_message TEXT NOT NULL,
2027
+ system_prompt_components TEXT NOT NULL,
2028
+ conversation_history TEXT NOT NULL,
2029
+ user_message TEXT NOT NULL,
2030
+ response_content TEXT NOT NULL,
2031
+ response_model TEXT NOT NULL,
2032
+ response_latency_ms INTEGER NOT NULL,
2033
+ response_skipped INTEGER NOT NULL DEFAULT 0,
2034
+ config_snapshot TEXT NOT NULL,
2035
+ token_est_system INTEGER NOT NULL DEFAULT 0,
2036
+ token_est_history INTEGER NOT NULL DEFAULT 0,
2037
+ token_est_user INTEGER NOT NULL DEFAULT 0,
2038
+ token_est_total INTEGER NOT NULL DEFAULT 0
2039
+ )
2040
+ `);
2041
+ this.db.exec(`
2042
+ CREATE INDEX IF NOT EXISTS idx_request_logs_timestamp ON request_logs(timestamp DESC);
2043
+ CREATE INDEX IF NOT EXISTS idx_request_logs_context ON request_logs(context);
2044
+ CREATE INDEX IF NOT EXISTS idx_request_logs_sender ON request_logs(sender_handle);
2045
+ `);
2046
+ }
2047
+ prune() {
2048
+ const cutoff = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString();
2049
+ const result = this.db.prepare("DELETE FROM request_logs WHERE timestamp < ?").run(cutoff);
2050
+ if (result.changes > 0) {
2051
+ console.log(`[dashboard] Pruned ${result.changes} old request logs`);
2052
+ }
2053
+ }
2054
+ log(entry) {
2055
+ const id = randomUUID();
2056
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
2057
+ const sysTokens = Math.ceil(entry.systemPromptComponents.fullText.length / 4);
2058
+ const histTokens = Math.ceil(
2059
+ entry.conversationHistory.reduce((sum, m) => sum + m.content.length, 0) / 4
2060
+ );
2061
+ const userTokens = Math.ceil(entry.userMessage.length / 4);
2062
+ this.db.prepare(
2063
+ `INSERT INTO request_logs (
2064
+ id, timestamp, context, context_switched, routing_reason,
2065
+ channel_id, channel_kind, sender_handle, raw_message,
2066
+ system_prompt_components, conversation_history, user_message,
2067
+ response_content, response_model, response_latency_ms, response_skipped,
2068
+ config_snapshot,
2069
+ token_est_system, token_est_history, token_est_user, token_est_total
2070
+ ) VALUES (
2071
+ ?, ?, ?, ?, ?,
2072
+ ?, ?, ?, ?,
2073
+ ?, ?, ?,
2074
+ ?, ?, ?, ?,
2075
+ ?,
2076
+ ?, ?, ?, ?
2077
+ )`
2078
+ ).run(
2079
+ id,
2080
+ timestamp,
2081
+ entry.context,
2082
+ entry.contextSwitched ? 1 : 0,
2083
+ entry.routingReason,
2084
+ entry.channelId ?? "",
2085
+ entry.channelKind ?? "",
2086
+ entry.senderHandle ?? "",
2087
+ entry.rawMessage,
2088
+ JSON.stringify(entry.systemPromptComponents),
2089
+ JSON.stringify(entry.conversationHistory),
2090
+ entry.userMessage,
2091
+ entry.response.content,
2092
+ entry.response.model,
2093
+ entry.response.latencyMs,
2094
+ entry.response.skipped ? 1 : 0,
2095
+ JSON.stringify(entry.config),
2096
+ sysTokens,
2097
+ histTokens,
2098
+ userTokens,
2099
+ sysTokens + histTokens + userTokens
2100
+ );
2101
+ return id;
2102
+ }
2103
+ getRequests(opts = {}) {
2104
+ const limit = opts.limit ?? 50;
2105
+ const offset = opts.offset ?? 0;
2106
+ const conditions = [];
2107
+ const params = [];
2108
+ if (opts.context) {
2109
+ conditions.push("context = ?");
2110
+ params.push(opts.context);
2111
+ }
2112
+ if (opts.sender) {
2113
+ conditions.push("sender_handle = ?");
2114
+ params.push(opts.sender);
2115
+ }
2116
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
2117
+ const total = this.db.prepare(`SELECT COUNT(*) as count FROM request_logs ${where}`).get(...params).count;
2118
+ const requests = this.db.prepare(
2119
+ `SELECT * FROM request_logs ${where} ORDER BY timestamp DESC LIMIT ? OFFSET ?`
2120
+ ).all(...params, limit, offset);
2121
+ return { requests, total };
2122
+ }
2123
+ getRequest(id) {
2124
+ return this.db.prepare("SELECT * FROM request_logs WHERE id = ?").get(id);
2125
+ }
2126
+ close() {
2127
+ this.db.close();
2128
+ }
2129
+ };
2130
+
2131
+ // packages/runtime/src/dashboard.ts
2132
+ import { createServer } from "http";
2133
+ import { readFileSync as readFileSync4 } from "fs";
2134
+ import { resolve as resolve4, dirname as dirname4 } from "path";
1749
2135
  import { fileURLToPath as fileURLToPath2 } from "url";
2136
+ var __dirname = dirname4(fileURLToPath2(import.meta.url));
2137
+ var DASHBOARD_PORT = 9485;
2138
+ var spaHtml = null;
2139
+ function getSpaHtml() {
2140
+ if (!spaHtml) {
2141
+ for (const dir of [__dirname, resolve4(__dirname, "../src")]) {
2142
+ try {
2143
+ spaHtml = readFileSync4(resolve4(dir, "dashboard.html"), "utf-8");
2144
+ break;
2145
+ } catch {
2146
+ }
2147
+ }
2148
+ if (!spaHtml) spaHtml = "<h1>Dashboard HTML not found</h1>";
2149
+ }
2150
+ return spaHtml;
2151
+ }
2152
+ function json(res, data, status = 200) {
2153
+ res.writeHead(status, {
2154
+ "Content-Type": "application/json",
2155
+ "Access-Control-Allow-Origin": "*"
2156
+ });
2157
+ res.end(JSON.stringify(data));
2158
+ }
2159
+ function parseQuery(url) {
2160
+ const idx = url.indexOf("?");
2161
+ if (idx === -1) return {};
2162
+ const params = {};
2163
+ for (const pair of url.slice(idx + 1).split("&")) {
2164
+ const [k, v] = pair.split("=");
2165
+ if (k) params[decodeURIComponent(k)] = decodeURIComponent(v ?? "");
2166
+ }
2167
+ return params;
2168
+ }
2169
+ async function proxyMemory(memoryUrl, path, method, res) {
2170
+ try {
2171
+ const resp = await fetch(`${memoryUrl}${path}`, { method });
2172
+ const body = await resp.text();
2173
+ res.writeHead(resp.status, {
2174
+ "Content-Type": resp.headers.get("content-type") ?? "application/json",
2175
+ "Access-Control-Allow-Origin": "*"
2176
+ });
2177
+ res.end(body);
2178
+ } catch (err) {
2179
+ json(res, { error: err.message }, 502);
2180
+ }
2181
+ }
2182
+ function startDashboardServer(requestLogger, memoryConfig) {
2183
+ const memoryUrl = memoryConfig.daemon_url;
2184
+ const server = createServer(async (req, res) => {
2185
+ const method = req.method ?? "GET";
2186
+ const rawUrl = req.url ?? "/";
2187
+ const urlPath = rawUrl.split("?")[0];
2188
+ if (method === "OPTIONS") {
2189
+ res.writeHead(204, {
2190
+ "Access-Control-Allow-Origin": "*",
2191
+ "Access-Control-Allow-Methods": "GET, DELETE, OPTIONS",
2192
+ "Access-Control-Allow-Headers": "Content-Type"
2193
+ });
2194
+ res.end();
2195
+ return;
2196
+ }
2197
+ try {
2198
+ if (method === "GET" && urlPath === "/") {
2199
+ res.writeHead(200, { "Content-Type": "text/html" });
2200
+ res.end(getSpaHtml());
2201
+ return;
2202
+ }
2203
+ if (method === "GET" && urlPath === "/api/requests") {
2204
+ const q = parseQuery(rawUrl);
2205
+ const result = requestLogger.getRequests({
2206
+ limit: q.limit ? parseInt(q.limit, 10) : void 0,
2207
+ offset: q.offset ? parseInt(q.offset, 10) : void 0,
2208
+ context: q.context || void 0,
2209
+ sender: q.sender || void 0
2210
+ });
2211
+ json(res, result);
2212
+ return;
2213
+ }
2214
+ const reqDetailMatch = urlPath.match(/^\/api\/requests\/([^/]+)$/);
2215
+ if (method === "GET" && reqDetailMatch) {
2216
+ const entry = requestLogger.getRequest(reqDetailMatch[1]);
2217
+ if (!entry) {
2218
+ json(res, { error: "Not found" }, 404);
2219
+ } else {
2220
+ json(res, entry);
2221
+ }
2222
+ return;
2223
+ }
2224
+ if (method === "GET" && urlPath === "/api/contexts") {
2225
+ await proxyMemory(memoryUrl, "/contexts", "GET", res);
2226
+ return;
2227
+ }
2228
+ const episodesMatch = urlPath.match(/^\/api\/contexts\/([^/]+)\/episodes$/);
2229
+ if (method === "GET" && episodesMatch) {
2230
+ const name = decodeURIComponent(episodesMatch[1]);
2231
+ await proxyMemory(memoryUrl, `/contexts/${encodeURIComponent(name)}`, "GET", res);
2232
+ return;
2233
+ }
2234
+ const l3Match = urlPath.match(/^\/api\/contexts\/([^/]+)\/l3$/);
2235
+ if (method === "GET" && l3Match) {
2236
+ const name = decodeURIComponent(l3Match[1]);
2237
+ await proxyMemory(
2238
+ memoryUrl,
2239
+ `/promotion/l3?context=${encodeURIComponent(name)}`,
2240
+ "GET",
2241
+ res
2242
+ );
2243
+ return;
2244
+ }
2245
+ const l3DeleteMatch = urlPath.match(/^\/api\/l3\/([^/]+)$/);
2246
+ if (method === "DELETE" && l3DeleteMatch) {
2247
+ const id = decodeURIComponent(l3DeleteMatch[1]);
2248
+ await proxyMemory(memoryUrl, `/promotion/l3/${encodeURIComponent(id)}`, "DELETE", res);
2249
+ return;
2250
+ }
2251
+ json(res, { error: "Not found" }, 404);
2252
+ } catch (err) {
2253
+ console.error("[dashboard] Request error:", err.message);
2254
+ json(res, { error: "Internal server error" }, 500);
2255
+ }
2256
+ });
2257
+ server.listen(DASHBOARD_PORT, "127.0.0.1", () => {
2258
+ console.log(
2259
+ `[hivemind] Dashboard listening on http://127.0.0.1:${DASHBOARD_PORT}`
2260
+ );
2261
+ });
2262
+ }
2263
+
2264
+ // packages/runtime/src/pipeline.ts
2265
+ import { readFileSync as readFileSync5, writeFileSync, unlinkSync } from "fs";
2266
+ import { resolve as resolve5, dirname as dirname5 } from "path";
2267
+ import { fileURLToPath as fileURLToPath3 } from "url";
1750
2268
  var PACKAGE_VERSION = "unknown";
1751
2269
  try {
1752
- const __dirname = dirname3(fileURLToPath2(import.meta.url));
1753
- const pkg = JSON.parse(readFileSync4(resolve4(__dirname, "../package.json"), "utf-8"));
2270
+ const __dirname2 = dirname5(fileURLToPath3(import.meta.url));
2271
+ const pkg = JSON.parse(readFileSync5(resolve5(__dirname2, "../package.json"), "utf-8"));
1754
2272
  PACKAGE_VERSION = pkg.version ?? "unknown";
1755
2273
  } catch {
1756
2274
  }
@@ -1758,7 +2276,7 @@ var sesameConnected = false;
1758
2276
  var memoryConnected = false;
1759
2277
  var startTime = Date.now();
1760
2278
  function startHealthServer(port) {
1761
- const server = createServer((req, res) => {
2279
+ const server = createServer2((req, res) => {
1762
2280
  if (req.method === "GET" && req.url === HEALTH_PATH) {
1763
2281
  const status = {
1764
2282
  status: sesameConnected ? "ok" : "degraded",
@@ -1812,7 +2330,10 @@ async function startPipeline(configPath) {
1812
2330
  memoryConnected = true;
1813
2331
  console.log("[hivemind] Memory daemon connected");
1814
2332
  }
2333
+ const requestLogger = new RequestLogger(resolve5(dirname5(configPath), "data", "dashboard.db"));
2334
+ startDashboardServer(requestLogger, config.memory);
1815
2335
  const agent = new Agent(config);
2336
+ agent.setRequestLogger(requestLogger);
1816
2337
  console.log(`[hivemind] Context manager initialized (active: ${agent.getActiveContext()})`);
1817
2338
  if (config.sesame.api_key) {
1818
2339
  await startSesameLoop(config, agent);
@@ -1927,13 +2448,13 @@ ${response.content}
1927
2448
  console.error("Error:", err.message);
1928
2449
  }
1929
2450
  });
1930
- return new Promise((resolve5) => {
1931
- rl.on("close", resolve5);
2451
+ return new Promise((resolve6) => {
2452
+ rl.on("close", resolve6);
1932
2453
  });
1933
2454
  }
1934
2455
 
1935
2456
  // packages/runtime/src/fleet/worker-server.ts
1936
- import { createServer as createServer2 } from "http";
2457
+ import { createServer as createServer3 } from "http";
1937
2458
  var WorkerServer = class {
1938
2459
  server = null;
1939
2460
  workerId;
@@ -1957,20 +2478,20 @@ var WorkerServer = class {
1957
2478
  }
1958
2479
  /** Start listening. */
1959
2480
  async start() {
1960
- return new Promise((resolve5, reject) => {
1961
- this.server = createServer2((req, res) => this.handleRequest(req, res));
2481
+ return new Promise((resolve6, reject) => {
2482
+ this.server = createServer3((req, res) => this.handleRequest(req, res));
1962
2483
  this.server.on("error", reject);
1963
- this.server.listen(this.port, () => resolve5());
2484
+ this.server.listen(this.port, () => resolve6());
1964
2485
  });
1965
2486
  }
1966
2487
  /** Stop the server. */
1967
2488
  async stop() {
1968
- return new Promise((resolve5) => {
2489
+ return new Promise((resolve6) => {
1969
2490
  if (!this.server) {
1970
- resolve5();
2491
+ resolve6();
1971
2492
  return;
1972
2493
  }
1973
- this.server.close(() => resolve5());
2494
+ this.server.close(() => resolve6());
1974
2495
  });
1975
2496
  }
1976
2497
  getPort() {
@@ -2093,10 +2614,10 @@ var WorkerServer = class {
2093
2614
  }
2094
2615
  };
2095
2616
  function readBody(req) {
2096
- return new Promise((resolve5, reject) => {
2617
+ return new Promise((resolve6, reject) => {
2097
2618
  const chunks = [];
2098
2619
  req.on("data", (chunk) => chunks.push(chunk));
2099
- req.on("end", () => resolve5(Buffer.concat(chunks).toString("utf-8")));
2620
+ req.on("end", () => resolve6(Buffer.concat(chunks).toString("utf-8")));
2100
2621
  req.on("error", reject);
2101
2622
  });
2102
2623
  }
@@ -2423,4 +2944,4 @@ smol-toml/dist/index.js:
2423
2944
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2424
2945
  *)
2425
2946
  */
2426
- //# sourceMappingURL=chunk-LB6MG36X.js.map
2947
+ //# sourceMappingURL=chunk-QAOBO4W4.js.map