@sesamespace/hivemind 0.6.0 → 0.6.1

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.
@@ -802,192 +802,6 @@ var Agent = class {
802
802
  let stripped = message.replace(/^\[.+?\s+in\s+group\s+chat\]:\s*/, "");
803
803
  stripped = stripped.replace(/^\[.+?\]:\s*/, "");
804
804
  const cmdText = stripped;
805
- if (/^hm[:\s]status\b/i.test(cmdText)) {
806
- const memoryOk = await this.memory.healthCheck();
807
- const contexts = this.contextManager.listContexts();
808
- let contextStats = "";
809
- try {
810
- const daemonContexts = await this.memory.listContexts();
811
- contextStats = daemonContexts.map((c) => ` - ${c.name}: ${c.episode_count} episodes`).join("\n");
812
- } catch {
813
- contextStats = " (unable to query memory daemon)";
814
- }
815
- const response = [
816
- `## Agent Status`,
817
- ``,
818
- `**Name:** ${this.config.agent.name}`,
819
- `**Active Context:** ${activeCtx}`,
820
- `**Memory Daemon:** ${memoryOk ? "\u2705 connected" : "\u274C unreachable"} (${this.config.memory.daemon_url})`,
821
- `**Model:** ${this.config.llm.model}`,
822
- `**Temperature:** ${this.config.llm.temperature}`,
823
- `**Max Tokens:** ${this.config.llm.max_tokens}`,
824
- `**Memory Top-K:** ${this.config.memory.top_k}`,
825
- `**Messages Processed:** ${this.messageCount}`,
826
- `**L1 Contexts in Memory:** ${this.conversationHistories.size}`,
827
- ``,
828
- `### Contexts`,
829
- contextStats
830
- ].join("\n");
831
- return { content: response, model: "system", context: activeCtx };
832
- }
833
- if (/^hm[:\s]memory\s+stats\b/i.test(cmdText)) {
834
- try {
835
- const contexts = await this.memory.listContexts();
836
- let totalEpisodes = 0;
837
- let totalL3 = 0;
838
- let lines = [];
839
- for (const ctx of contexts) {
840
- totalEpisodes += ctx.episode_count;
841
- let l3Count = 0;
842
- try {
843
- const l3 = await this.memory.getL3Knowledge(ctx.name);
844
- l3Count = l3.length;
845
- totalL3 += l3Count;
846
- } catch {
847
- }
848
- lines.push(` - **${ctx.name}**: ${ctx.episode_count} L2 episodes, ${l3Count} L3 entries`);
849
- }
850
- const response = [
851
- `## Memory Stats`,
852
- ``,
853
- `**Total L2 Episodes:** ${totalEpisodes}`,
854
- `**Total L3 Knowledge:** ${totalL3}`,
855
- `**Contexts:** ${contexts.length}`,
856
- ``,
857
- `### Per Context`,
858
- ...lines
859
- ].join("\n");
860
- return { content: response, model: "system", context: activeCtx };
861
- } catch (err) {
862
- return { content: `Memory stats failed: ${err.message}`, model: "system", context: activeCtx };
863
- }
864
- }
865
- const memSearchMatch = cmdText.match(/^hm[:\s]memory\s+search\s+(.+)/i);
866
- if (memSearchMatch) {
867
- const query = memSearchMatch[1].trim();
868
- try {
869
- const results = await this.memory.search(query, activeCtx, this.config.memory.top_k);
870
- if (results.length === 0) {
871
- return { content: `No memories found for "${query}" in context "${activeCtx}".`, model: "system", context: activeCtx };
872
- }
873
- let response = `## Memory Search: "${query}"
874
-
875
- `;
876
- for (const ep of results) {
877
- const ctxLabel = ep.context_name !== activeCtx ? ` [from: ${ep.context_name}]` : "";
878
- response += `- **[${ep.role}]** (score: ${ep.score.toFixed(3)})${ctxLabel} ${ep.content.slice(0, 300)}${ep.content.length > 300 ? "..." : ""}
879
- `;
880
- }
881
- return { content: response, model: "system", context: activeCtx };
882
- } catch (err) {
883
- return { content: `Memory search failed: ${err.message}`, model: "system", context: activeCtx };
884
- }
885
- }
886
- const l3Match = cmdText.match(/^hm[:\s]memory\s+l3(?:\s+(\S+))?/i);
887
- if (l3Match) {
888
- const targetCtx = l3Match[1] || activeCtx;
889
- try {
890
- const entries = await this.memory.getL3Knowledge(targetCtx);
891
- if (entries.length === 0) {
892
- return { content: `No L3 knowledge in context "${targetCtx}".`, model: "system", context: activeCtx };
893
- }
894
- let response = `## L3 Knowledge: ${targetCtx}
895
-
896
- `;
897
- for (const entry of entries) {
898
- response += `- **[${entry.id.slice(0, 8)}]** (density: ${entry.connection_density}, accesses: ${entry.access_count})
899
- ${entry.content}
900
- `;
901
- }
902
- return { content: response, model: "system", context: activeCtx };
903
- } catch (err) {
904
- return { content: `L3 query failed: ${err.message}`, model: "system", context: activeCtx };
905
- }
906
- }
907
- if (/^hm[:\s]config\b/i.test(cmdText)) {
908
- const response = [
909
- `## Configuration`,
910
- ``,
911
- `**Agent:** ${this.config.agent.name}`,
912
- `**Personality:** ${this.config.agent.personality}`,
913
- `**Workspace:** ${this.config.agent.workspace || "(none)"}`,
914
- ``,
915
- `### LLM`,
916
- `- Model: ${this.config.llm.model}`,
917
- `- Base URL: ${this.config.llm.base_url}`,
918
- `- Max Tokens: ${this.config.llm.max_tokens}`,
919
- `- Temperature: ${this.config.llm.temperature}`,
920
- ``,
921
- `### Memory`,
922
- `- Daemon URL: ${this.config.memory.daemon_url}`,
923
- `- Top-K: ${this.config.memory.top_k}`,
924
- `- Embedding Model: ${this.config.memory.embedding_model}`,
925
- ``,
926
- `### Sesame`,
927
- `- API: ${this.config.sesame.api_url}`,
928
- `- WebSocket: ${this.config.sesame.ws_url}`,
929
- `- API Key: ${this.config.sesame.api_key ? "***" + this.config.sesame.api_key.slice(-4) : "(not set)"}`
930
- ].join("\n");
931
- return { content: response, model: "system", context: activeCtx };
932
- }
933
- if (/^hm[:\s]contexts\b/i.test(cmdText)) {
934
- const localContexts = this.contextManager.listContexts();
935
- let daemonInfo = "";
936
- try {
937
- const daemonContexts = await this.memory.listContexts();
938
- daemonInfo = "\n### Memory Daemon Contexts\n" + daemonContexts.map(
939
- (c) => ` - **${c.name}**: ${c.episode_count} episodes (created: ${c.created_at})`
940
- ).join("\n");
941
- } catch {
942
- daemonInfo = "\n(Memory daemon unreachable)";
943
- }
944
- const localInfo = localContexts.map((c) => {
945
- const active = c.name === activeCtx ? " \u2190 active" : "";
946
- const historyLen = this.conversationHistories.get(c.name)?.length || 0;
947
- return ` - **${c.name}**${active}: ${historyLen / 2} turns in L1`;
948
- }).join("\n");
949
- const response = [
950
- `## Contexts`,
951
- ``,
952
- `**Active:** ${activeCtx}`,
953
- ``,
954
- `### Local (L1 Working Memory)`,
955
- localInfo,
956
- daemonInfo
957
- ].join("\n");
958
- return { content: response, model: "system", context: activeCtx };
959
- }
960
- if (/^hm[:\s]help\b/i.test(cmdText)) {
961
- const response = [
962
- `## Available Commands`,
963
- ``,
964
- `### Introspection`,
965
- `- \`hm:status\` \u2014 Agent health, memory status, config summary`,
966
- `- \`hm:config\` \u2014 Full configuration details`,
967
- `- \`hm:contexts\` \u2014 List all contexts with L1/L2 info`,
968
- `- \`hm:memory stats\` \u2014 Episode counts, L3 entries per context`,
969
- `- \`hm:memory search <query>\` \u2014 Search L2 memories with scores`,
970
- `- \`hm:memory l3 [context]\` \u2014 Show L3 knowledge entries`,
971
- `- \`hm:help\` \u2014 This help message`,
972
- ``,
973
- `### Context Management`,
974
- `- \`switch to <name>\` \u2014 Switch active context`,
975
- `- \`create context <name>\` \u2014 Create a new context`,
976
- `- \`list contexts\` \u2014 List contexts (legacy)`,
977
- ``,
978
- `### Memory`,
979
- `- \`search all: <query>\` \u2014 Cross-context search`,
980
- `- \`share <episode_id> with <context>\` \u2014 Share episode across contexts`,
981
- ``,
982
- `### Tasks`,
983
- `- \`task add <title>\` \u2014 Create a task`,
984
- `- \`task list [status]\` \u2014 List tasks`,
985
- `- \`task start <id>\` \u2014 Start a task`,
986
- `- \`task complete <id>\` \u2014 Complete a task`,
987
- `- \`task next\` \u2014 Pick up next available task`
988
- ].join("\n");
989
- return { content: response, model: "system", context: activeCtx };
990
- }
991
805
  const searchAllMatch = cmdText.match(/^(?:search\s+all|cross-context\s+search)[:\s]+(.+)/i);
992
806
  if (searchAllMatch) {
993
807
  const query = searchAllMatch[1].trim();
@@ -1947,12 +1761,7 @@ var SesameClient2 = class {
1947
1761
  this.sdk.on("message", (event) => {
1948
1762
  const msg = event.data || event.message || event;
1949
1763
  const senderId = msg.senderId || msg.sender?.id;
1950
- if (senderId === this.agentId) {
1951
- const content = msg.content?.trim() || "";
1952
- const isHmCommand = /^hm[:\s]/i.test(content);
1953
- const isTextCommand = /^(?:list contexts|switch to|create context|search all:|task )/i.test(content);
1954
- if (!isHmCommand && !isTextCommand) return;
1955
- }
1764
+ if (senderId === this.agentId) return;
1956
1765
  if (!this.messageHandler || !msg.content) return;
1957
1766
  const channelInfo = this.channels.get(msg.channelId);
1958
1767
  this.messageHandler({
@@ -2664,6 +2473,157 @@ function registerWebTools(registry, config) {
2664
2473
  );
2665
2474
  }
2666
2475
 
2476
+ // packages/runtime/src/tools/memory.ts
2477
+ function registerMemoryTools(registry, daemonUrl) {
2478
+ registry.register(
2479
+ "memory_search",
2480
+ "Search your episodic memory for relevant past conversations and experiences. Returns scored results from your memory system. Use this when you want to recall something from a previous conversation or check what you know about a topic.",
2481
+ {
2482
+ type: "object",
2483
+ properties: {
2484
+ query: {
2485
+ type: "string",
2486
+ description: "What to search for in memory"
2487
+ },
2488
+ context: {
2489
+ type: "string",
2490
+ description: "Context to search in (default: current active context)"
2491
+ },
2492
+ top_k: {
2493
+ type: "number",
2494
+ description: "Number of results to return (default: 10)"
2495
+ }
2496
+ },
2497
+ required: ["query"]
2498
+ },
2499
+ async (params) => {
2500
+ const query = params.query;
2501
+ const context = params.context || "global";
2502
+ const topK = params.top_k || 10;
2503
+ try {
2504
+ const resp = await fetch(`${daemonUrl}/api/search`, {
2505
+ method: "POST",
2506
+ headers: { "Content-Type": "application/json" },
2507
+ body: JSON.stringify({ query, context_name: context, top_k: topK })
2508
+ });
2509
+ if (!resp.ok) return `Memory search failed: ${resp.status}`;
2510
+ const data = await resp.json();
2511
+ if (!data.results?.length) return `No memories found for "${query}" in context "${context}".`;
2512
+ return data.results.map((r, i) => `${i + 1}. [score: ${r.score.toFixed(3)}] [${r.context_name}] [${r.role}] ${r.content.slice(0, 200)}`).join("\n");
2513
+ } catch (err) {
2514
+ return `Memory daemon unreachable: ${err.message}`;
2515
+ }
2516
+ }
2517
+ );
2518
+ registry.register(
2519
+ "memory_stats",
2520
+ "Get statistics about your memory system \u2014 episode counts, contexts, L3 knowledge entries. Use for self-diagnosis or understanding your memory state.",
2521
+ {
2522
+ type: "object",
2523
+ properties: {},
2524
+ required: []
2525
+ },
2526
+ async () => {
2527
+ try {
2528
+ const healthResp = await fetch(`${daemonUrl}/health`);
2529
+ const health = healthResp.ok ? await healthResp.json() : { status: "unreachable" };
2530
+ const ctxResp = await fetch(`${daemonUrl}/api/contexts`);
2531
+ const contexts = ctxResp.ok ? (await ctxResp.json()).contexts : [];
2532
+ const l3Counts = {};
2533
+ for (const ctx of contexts) {
2534
+ try {
2535
+ const l3Resp = await fetch(`${daemonUrl}/api/l3/${encodeURIComponent(ctx.name)}`);
2536
+ if (l3Resp.ok) {
2537
+ const l3Data = await l3Resp.json();
2538
+ l3Counts[ctx.name] = l3Data.entries?.length ?? 0;
2539
+ }
2540
+ } catch {
2541
+ }
2542
+ }
2543
+ const totalEpisodes = contexts.reduce((sum, c) => sum + c.episode_count, 0);
2544
+ const totalL3 = Object.values(l3Counts).reduce((sum, c) => sum + c, 0);
2545
+ let output = `Memory Daemon: ${health.status || "unknown"}
2546
+ `;
2547
+ output += `Total Episodes (L2): ${totalEpisodes}
2548
+ `;
2549
+ output += `Total Knowledge (L3): ${totalL3}
2550
+ `;
2551
+ output += `
2552
+ Contexts:
2553
+ `;
2554
+ for (const ctx of contexts) {
2555
+ output += ` - ${ctx.name}: ${ctx.episode_count} episodes, ${l3Counts[ctx.name] ?? 0} L3 entries
2556
+ `;
2557
+ }
2558
+ return output;
2559
+ } catch (err) {
2560
+ return `Memory daemon unreachable: ${err.message}`;
2561
+ }
2562
+ }
2563
+ );
2564
+ registry.register(
2565
+ "memory_l3",
2566
+ "View your promoted L3 knowledge \u2014 high-level patterns, decisions, and insights that have been distilled from your episodic memories. This is your 'long-term wisdom'.",
2567
+ {
2568
+ type: "object",
2569
+ properties: {
2570
+ context: {
2571
+ type: "string",
2572
+ description: "Context to get L3 knowledge for (default: global)"
2573
+ }
2574
+ },
2575
+ required: []
2576
+ },
2577
+ async (params) => {
2578
+ const context = params.context || "global";
2579
+ try {
2580
+ const resp = await fetch(`${daemonUrl}/api/l3/${encodeURIComponent(context)}`);
2581
+ if (!resp.ok) return `L3 query failed: ${resp.status}`;
2582
+ const data = await resp.json();
2583
+ if (!data.entries?.length) return `No L3 knowledge in context "${context}".`;
2584
+ return data.entries.map((e, i) => `${i + 1}. ${e.content} (from ${e.source_episodes} episodes, ${e.created_at})`).join("\n");
2585
+ } catch (err) {
2586
+ return `Memory daemon unreachable: ${err.message}`;
2587
+ }
2588
+ }
2589
+ );
2590
+ registry.register(
2591
+ "memory_cross_search",
2592
+ "Search across ALL memory contexts at once. Useful when you're not sure which context a memory belongs to.",
2593
+ {
2594
+ type: "object",
2595
+ properties: {
2596
+ query: {
2597
+ type: "string",
2598
+ description: "What to search for across all contexts"
2599
+ },
2600
+ top_k: {
2601
+ type: "number",
2602
+ description: "Number of results (default: 10)"
2603
+ }
2604
+ },
2605
+ required: ["query"]
2606
+ },
2607
+ async (params) => {
2608
+ const query = params.query;
2609
+ const topK = params.top_k || 10;
2610
+ try {
2611
+ const resp = await fetch(`${daemonUrl}/api/search/cross-context`, {
2612
+ method: "POST",
2613
+ headers: { "Content-Type": "application/json" },
2614
+ body: JSON.stringify({ query, top_k: topK })
2615
+ });
2616
+ if (!resp.ok) return `Cross-context search failed: ${resp.status}`;
2617
+ const data = await resp.json();
2618
+ if (!data.results?.length) return `No memories found across any context for "${query}".`;
2619
+ return data.results.map((r, i) => `${i + 1}. [${r.score.toFixed(3)}] [${r.context_name}] ${r.content.slice(0, 200)}`).join("\n");
2620
+ } catch (err) {
2621
+ return `Memory daemon unreachable: ${err.message}`;
2622
+ }
2623
+ }
2624
+ );
2625
+ }
2626
+
2667
2627
  // packages/runtime/src/tools/register.ts
2668
2628
  import { resolve as resolve7 } from "path";
2669
2629
  import { mkdirSync as mkdirSync3, existsSync as existsSync5 } from "fs";
@@ -2679,6 +2639,7 @@ function registerAllTools(hivemindHome, config) {
2679
2639
  registerShellTool(registry, workspaceDir);
2680
2640
  registerFileTools(registry, workspaceDir);
2681
2641
  registerWebTools(registry, { braveApiKey: config?.braveApiKey });
2642
+ registerMemoryTools(registry, config?.memoryDaemonUrl || "http://localhost:3434");
2682
2643
  return registry;
2683
2644
  }
2684
2645
 
@@ -2759,7 +2720,8 @@ async function startPipeline(configPath) {
2759
2720
  const toolRegistry = registerAllTools(hivemindHome, {
2760
2721
  enabled: true,
2761
2722
  workspace: config.agent.workspace || "workspace",
2762
- braveApiKey: process.env.BRAVE_API_KEY
2723
+ braveApiKey: process.env.BRAVE_API_KEY,
2724
+ memoryDaemonUrl: config.memory.daemon_url
2763
2725
  });
2764
2726
  agent.setToolRegistry(toolRegistry);
2765
2727
  console.log(`[hivemind] Context manager initialized (active: ${agent.getActiveContext()})`);
@@ -3405,4 +3367,4 @@ smol-toml/dist/index.js:
3405
3367
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3406
3368
  *)
3407
3369
  */
3408
- //# sourceMappingURL=chunk-LQ7CYHSQ.js.map
3370
+ //# sourceMappingURL=chunk-7YHRRM5B.js.map