@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.
- package/.pnpmrc.json +1 -0
- package/DASHBOARD-PLAN.md +206 -0
- package/dist/{chunk-7HIU4UF2.js → chunk-7GZ545B5.js} +2 -2
- package/dist/{chunk-UJDDOQEE.js → chunk-AGFKYYWT.js} +2 -2
- package/dist/{chunk-LB6MG36X.js → chunk-QAOBO4W4.js} +551 -30
- package/dist/chunk-QAOBO4W4.js.map +1 -0
- package/dist/{chunk-3GTSMZNL.js → chunk-TRXLCIB4.js} +2 -2
- package/dist/{chunk-KUFSIVPF.js → chunk-YMQR5I6C.js} +3 -3
- package/dist/commands/fleet.js +3 -3
- package/dist/commands/start.js +3 -3
- package/dist/commands/watchdog.js +3 -3
- package/dist/index.js +2 -2
- package/dist/main.js +5 -5
- package/dist/start.js +1 -1
- package/package.json +7 -1
- package/dist/chunk-LB6MG36X.js.map +0 -1
- /package/dist/{chunk-7HIU4UF2.js.map → chunk-7GZ545B5.js.map} +0 -0
- /package/dist/{chunk-UJDDOQEE.js.map → chunk-AGFKYYWT.js.map} +0 -0
- /package/dist/{chunk-3GTSMZNL.js.map → chunk-TRXLCIB4.js.map} +0 -0
- /package/dist/{chunk-KUFSIVPF.js.map → chunk-YMQR5I6C.js.map} +0 -0
|
@@ -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
|
-
|
|
522
|
+
let wsBlock = "\n# Workspace Context\n";
|
|
522
523
|
for (const [filename, content] of wsFiles) {
|
|
523
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
641
|
-
const messages = buildMessages(
|
|
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
|
-
|
|
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 =
|
|
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(
|
|
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
|
|
1578
|
-
const pkg = JSON.parse(readFileSync3(resolve3(
|
|
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/
|
|
1747
|
-
import
|
|
1748
|
-
import {
|
|
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
|
|
1753
|
-
const pkg = JSON.parse(
|
|
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 =
|
|
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((
|
|
1931
|
-
rl.on("close",
|
|
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
|
|
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((
|
|
1961
|
-
this.server =
|
|
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, () =>
|
|
2484
|
+
this.server.listen(this.port, () => resolve6());
|
|
1964
2485
|
});
|
|
1965
2486
|
}
|
|
1966
2487
|
/** Stop the server. */
|
|
1967
2488
|
async stop() {
|
|
1968
|
-
return new Promise((
|
|
2489
|
+
return new Promise((resolve6) => {
|
|
1969
2490
|
if (!this.server) {
|
|
1970
|
-
|
|
2491
|
+
resolve6();
|
|
1971
2492
|
return;
|
|
1972
2493
|
}
|
|
1973
|
-
this.server.close(() =>
|
|
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((
|
|
2617
|
+
return new Promise((resolve6, reject) => {
|
|
2097
2618
|
const chunks = [];
|
|
2098
2619
|
req.on("data", (chunk) => chunks.push(chunk));
|
|
2099
|
-
req.on("end", () =>
|
|
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-
|
|
2947
|
+
//# sourceMappingURL=chunk-QAOBO4W4.js.map
|