@miriad-systems/nuum 0.1.12 → 0.1.13

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 (2) hide show
  1. package/dist/index.js +571 -185
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -34411,6 +34411,7 @@ var activity = {
34411
34411
  ltmCurator: new ActivityLog("ltm-curator"),
34412
34412
  distillation: new ActivityLog("distillation"),
34413
34413
  reflection: new ActivityLog("reflection"),
34414
+ research: new ActivityLog("research"),
34414
34415
  server: new ActivityLog("server"),
34415
34416
  mcp: new ActivityLog("mcp")
34416
34417
  };
@@ -35315,7 +35316,54 @@ async function buildAgentContext(storage) {
35315
35316
  };
35316
35317
  }
35317
35318
 
35318
- // src/tool/reflection-search.ts
35319
+ // src/sub-agent/index.ts
35320
+ var log8 = Log.create({ service: "sub-agent" });
35321
+ async function runSubAgent(storage, config) {
35322
+ const {
35323
+ name: name17,
35324
+ taskPrompt,
35325
+ tools,
35326
+ finishToolName,
35327
+ extractResult,
35328
+ tier = "workhorse",
35329
+ maxTurns = 20,
35330
+ maxTokens = 4096,
35331
+ temperature = 0,
35332
+ onToolResult
35333
+ } = config;
35334
+ log8.info(`starting sub-agent: ${name17}`, { maxTurns, tier });
35335
+ const ctx = await buildAgentContext(storage);
35336
+ const model = Provider.getModelForTier(tier);
35337
+ const initialMessages = [
35338
+ ...ctx.historyTurns,
35339
+ { role: "user", content: `[SYSTEM TASK]
35340
+
35341
+ ${taskPrompt}` }
35342
+ ];
35343
+ const loopResult = await runAgentLoop({
35344
+ model,
35345
+ systemPrompt: ctx.systemPrompt,
35346
+ initialMessages,
35347
+ tools,
35348
+ maxTokens,
35349
+ temperature,
35350
+ maxTurns,
35351
+ isDone: stopOnTool(finishToolName),
35352
+ onToolResult
35353
+ });
35354
+ log8.info(`sub-agent complete: ${name17}`, {
35355
+ turnsUsed: loopResult.turnsUsed,
35356
+ stopReason: loopResult.stopReason
35357
+ });
35358
+ return {
35359
+ result: extractResult(),
35360
+ turnsUsed: loopResult.turnsUsed,
35361
+ usage: loopResult.usage,
35362
+ stopReason: loopResult.stopReason
35363
+ };
35364
+ }
35365
+
35366
+ // src/reflection/tools.ts
35319
35367
  function buildSearchMessagesTool(ctx) {
35320
35368
  const { storage } = ctx;
35321
35369
  return tool({
@@ -35373,19 +35421,6 @@ ${marker17}${m.content}`;
35373
35421
  }
35374
35422
  });
35375
35423
  }
35376
- function buildFinishReflectionTool(setAnswer) {
35377
- return tool({
35378
- description: "Complete the reflection and return your answer to the main agent.",
35379
- parameters: exports_external.object({
35380
- answer: exports_external.string().describe("Your answer or research findings. Be specific and include relevant details, quotes, or references.")
35381
- }),
35382
- execute: async ({ answer }) => {
35383
- activity.reflection.toolResult("finish_reflection", `${answer.length} chars`);
35384
- setAnswer(answer);
35385
- return "Reflection complete.";
35386
- }
35387
- });
35388
- }
35389
35424
  function createLTMContext(storage, toolCallId) {
35390
35425
  const baseCtx = Tool.createContext({
35391
35426
  sessionID: "reflection",
@@ -35410,7 +35445,7 @@ function wrapLTMTool(ltmTool, toolName, storage, summarizeResult) {
35410
35445
  }
35411
35446
  });
35412
35447
  }
35413
- function buildReflectionSearchTools(ctx) {
35448
+ function buildReflectionTools(ctx) {
35414
35449
  let answer = null;
35415
35450
  const tools = {
35416
35451
  search_messages: buildSearchMessagesTool(ctx),
@@ -35419,8 +35454,16 @@ function buildReflectionSearchTools(ctx) {
35419
35454
  ltm_read: wrapLTMTool(LTMReadTool, "ltm_read", ctx.storage, (output) => output.slice(0, 50)),
35420
35455
  ltm_glob: wrapLTMTool(LTMGlobTool, "ltm_glob", ctx.storage, (output) => `${output.split(`
35421
35456
  `).length} entries`),
35422
- finish_reflection: buildFinishReflectionTool((ans) => {
35423
- answer = ans;
35457
+ finish_reflection: tool({
35458
+ description: "Complete the reflection and return your answer to the main agent.",
35459
+ parameters: exports_external.object({
35460
+ answer: exports_external.string().describe("Your answer or research findings. Be specific and include relevant details, quotes, or references.")
35461
+ }),
35462
+ execute: async ({ answer: ans }) => {
35463
+ activity.reflection.toolResult("finish_reflection", `${ans.length} chars`);
35464
+ answer = ans;
35465
+ return "Reflection complete.";
35466
+ }
35424
35467
  })
35425
35468
  };
35426
35469
  return {
@@ -35430,8 +35473,6 @@ function buildReflectionSearchTools(ctx) {
35430
35473
  }
35431
35474
 
35432
35475
  // src/reflection/index.ts
35433
- var log8 = Log.create({ service: "reflection-agent" });
35434
- var MAX_REFLECTION_TURNS = 20;
35435
35476
  function buildReflectionPrompt(question) {
35436
35477
  return `## Reflection Mode
35437
35478
 
@@ -35459,39 +35500,24 @@ I'll search my memory now. Once I have enough information, I MUST call finish_re
35459
35500
  }
35460
35501
  async function runReflection(storage, question) {
35461
35502
  activity.reflection.start("Memory research", { question: question.slice(0, 50) });
35462
- const ctx = await buildAgentContext(storage);
35463
- const model = Provider.getModelForTier("workhorse");
35464
- const { tools, getAnswer } = buildReflectionSearchTools({ storage });
35465
- const taskPrompt = buildReflectionPrompt(question);
35466
- const initialMessages = [
35467
- ...ctx.historyTurns,
35468
- { role: "user", content: `[SYSTEM TASK]
35469
-
35470
- ${taskPrompt}` }
35471
- ];
35472
- const loopResult = await runAgentLoop({
35473
- model,
35474
- systemPrompt: ctx.systemPrompt,
35475
- initialMessages,
35503
+ const { tools, getAnswer } = buildReflectionTools({ storage });
35504
+ const result = await runSubAgent(storage, {
35505
+ name: "reflection",
35506
+ taskPrompt: buildReflectionPrompt(question),
35476
35507
  tools,
35477
- maxTokens: 4096,
35478
- temperature: 0,
35479
- maxTurns: MAX_REFLECTION_TURNS,
35480
- isDone: stopOnTool("finish_reflection")
35481
- });
35482
- const answer = getAnswer();
35483
- if (!answer) {
35484
- log8.warn("reflection agent did not call finish_reflection", {
35485
- turnsUsed: loopResult.turnsUsed
35486
- });
35487
- }
35488
- const result = {
35489
- answer: answer ?? loopResult.finalText ?? "Unable to find relevant information.",
35490
- turnsUsed: loopResult.turnsUsed,
35491
- usage: loopResult.usage
35508
+ finishToolName: "finish_reflection",
35509
+ extractResult: getAnswer,
35510
+ tier: "workhorse",
35511
+ maxTurns: 20,
35512
+ maxTokens: 4096
35513
+ });
35514
+ const answer = result.result ?? "Unable to find relevant information.";
35515
+ activity.reflection.complete(`${result.turnsUsed} turns, ${answer.length} chars`);
35516
+ return {
35517
+ answer,
35518
+ turnsUsed: result.turnsUsed,
35519
+ usage: result.usage
35492
35520
  };
35493
- activity.reflection.complete(`${result.turnsUsed} turns, ${result.answer.length} chars`);
35494
- return result;
35495
35521
  }
35496
35522
 
35497
35523
  // src/tool/reflect.ts
@@ -35556,6 +35582,427 @@ var ReflectTool = Tool.define("reflect", {
35556
35582
  }
35557
35583
  }
35558
35584
  });
35585
+ // src/research/tools.ts
35586
+ var AGENT_TYPE = "research";
35587
+ function buildResearchTools(storage) {
35588
+ const results = new Map;
35589
+ const createLTMContext2 = (toolCallId) => {
35590
+ const ctx = Tool.createContext({
35591
+ sessionID: "research",
35592
+ messageID: "research",
35593
+ callID: toolCallId
35594
+ });
35595
+ ctx.extra = {
35596
+ ltm: storage.ltm,
35597
+ agentType: AGENT_TYPE
35598
+ };
35599
+ return ctx;
35600
+ };
35601
+ const createBaseContext = (toolCallId) => {
35602
+ return Tool.createContext({
35603
+ sessionID: "research",
35604
+ messageID: "research",
35605
+ callID: toolCallId
35606
+ });
35607
+ };
35608
+ const tools = {};
35609
+ tools.ltm_glob = tool({
35610
+ description: LTMGlobTool.definition.description,
35611
+ parameters: LTMGlobTool.definition.parameters,
35612
+ execute: async (args, { toolCallId }) => {
35613
+ const toolResult = await LTMGlobTool.definition.execute(args, createLTMContext2(toolCallId));
35614
+ results.set(toolCallId, { output: toolResult.output, done: false });
35615
+ return toolResult.output;
35616
+ }
35617
+ });
35618
+ tools.ltm_search = tool({
35619
+ description: LTMSearchTool.definition.description,
35620
+ parameters: LTMSearchTool.definition.parameters,
35621
+ execute: async (args, { toolCallId }) => {
35622
+ const toolResult = await LTMSearchTool.definition.execute(args, createLTMContext2(toolCallId));
35623
+ results.set(toolCallId, { output: toolResult.output, done: false });
35624
+ return toolResult.output;
35625
+ }
35626
+ });
35627
+ tools.ltm_read = tool({
35628
+ description: LTMReadTool.definition.description,
35629
+ parameters: LTMReadTool.definition.parameters,
35630
+ execute: async (args, { toolCallId }) => {
35631
+ const toolResult = await LTMReadTool.definition.execute(args, createLTMContext2(toolCallId));
35632
+ results.set(toolCallId, { output: toolResult.output, done: false });
35633
+ return toolResult.output;
35634
+ }
35635
+ });
35636
+ tools.ltm_create = tool({
35637
+ description: LTMCreateTool.definition.description,
35638
+ parameters: LTMCreateTool.definition.parameters,
35639
+ execute: async (args, { toolCallId }) => {
35640
+ const toolResult = await LTMCreateTool.definition.execute(args, createLTMContext2(toolCallId));
35641
+ const entryCreated = toolResult.output.startsWith("Created entry:");
35642
+ results.set(toolCallId, {
35643
+ output: toolResult.output,
35644
+ done: false,
35645
+ entryCreated,
35646
+ slug: args.slug,
35647
+ title: args.title
35648
+ });
35649
+ return toolResult.output;
35650
+ }
35651
+ });
35652
+ tools.ltm_update = tool({
35653
+ description: LTMUpdateTool.definition.description,
35654
+ parameters: LTMUpdateTool.definition.parameters,
35655
+ execute: async (args, { toolCallId }) => {
35656
+ const toolResult = await LTMUpdateTool.definition.execute(args, createLTMContext2(toolCallId));
35657
+ const entryUpdated = toolResult.output.startsWith("Updated entry:");
35658
+ results.set(toolCallId, {
35659
+ output: toolResult.output,
35660
+ done: false,
35661
+ entryUpdated,
35662
+ slug: args.slug
35663
+ });
35664
+ return toolResult.output;
35665
+ }
35666
+ });
35667
+ tools.ltm_edit = tool({
35668
+ description: LTMEditTool.definition.description,
35669
+ parameters: LTMEditTool.definition.parameters,
35670
+ execute: async (args, { toolCallId }) => {
35671
+ const toolResult = await LTMEditTool.definition.execute(args, createLTMContext2(toolCallId));
35672
+ const entryUpdated = toolResult.output.startsWith("Edited entry:");
35673
+ results.set(toolCallId, {
35674
+ output: toolResult.output,
35675
+ done: false,
35676
+ entryUpdated,
35677
+ slug: args.slug
35678
+ });
35679
+ return toolResult.output;
35680
+ }
35681
+ });
35682
+ tools.ltm_reparent = tool({
35683
+ description: LTMReparentTool.definition.description,
35684
+ parameters: LTMReparentTool.definition.parameters,
35685
+ execute: async (args, { toolCallId }) => {
35686
+ const toolResult = await LTMReparentTool.definition.execute(args, createLTMContext2(toolCallId));
35687
+ const entryUpdated = toolResult.output.startsWith("Moved entry:");
35688
+ results.set(toolCallId, {
35689
+ output: toolResult.output,
35690
+ done: false,
35691
+ entryUpdated,
35692
+ slug: args.slug
35693
+ });
35694
+ return toolResult.output;
35695
+ }
35696
+ });
35697
+ tools.ltm_archive = tool({
35698
+ description: LTMArchiveTool.definition.description,
35699
+ parameters: LTMArchiveTool.definition.parameters,
35700
+ execute: async (args, { toolCallId }) => {
35701
+ const toolResult = await LTMArchiveTool.definition.execute(args, createLTMContext2(toolCallId));
35702
+ const entryArchived = toolResult.output.startsWith("Archived entry:");
35703
+ results.set(toolCallId, {
35704
+ output: toolResult.output,
35705
+ done: false,
35706
+ entryArchived,
35707
+ slug: args.slug
35708
+ });
35709
+ return toolResult.output;
35710
+ }
35711
+ });
35712
+ tools.search_messages = tool({
35713
+ description: "Search conversation history using full-text search. Returns message snippets with matches highlighted.",
35714
+ parameters: exports_external.object({
35715
+ query: exports_external.string().describe("Search query - keywords to find in messages"),
35716
+ limit: exports_external.number().optional().describe("Maximum results to return (default: 20)")
35717
+ }),
35718
+ execute: async ({ query, limit }, { toolCallId }) => {
35719
+ const searchResults = await storage.temporal.searchFTS(query, limit ?? 20);
35720
+ if (searchResults.length === 0) {
35721
+ const output2 = `No messages found matching "${query}"`;
35722
+ results.set(toolCallId, { output: output2, done: false });
35723
+ return output2;
35724
+ }
35725
+ const formatted = searchResults.map((r) => `[${r.id}] (${r.type}) ${r.snippet}`).join(`
35726
+
35727
+ `);
35728
+ const output = `Found ${searchResults.length} messages:
35729
+
35730
+ ${formatted}`;
35731
+ results.set(toolCallId, { output, done: false });
35732
+ return output;
35733
+ }
35734
+ });
35735
+ tools.get_message = tool({
35736
+ description: "Get a specific message by ID, optionally with surrounding context messages.",
35737
+ parameters: exports_external.object({
35738
+ id: exports_external.string().describe("Message ID (e.g., msg_01ABC...)"),
35739
+ contextBefore: exports_external.number().optional().describe("Number of messages to include before (default: 0)"),
35740
+ contextAfter: exports_external.number().optional().describe("Number of messages to include after (default: 0)")
35741
+ }),
35742
+ execute: async ({ id, contextBefore, contextAfter }, { toolCallId }) => {
35743
+ const messages = await storage.temporal.getMessageWithContext({
35744
+ id,
35745
+ contextBefore: contextBefore ?? 0,
35746
+ contextAfter: contextAfter ?? 0
35747
+ });
35748
+ if (messages.length === 0) {
35749
+ const output = `Message not found: ${id}`;
35750
+ results.set(toolCallId, { output, done: false });
35751
+ return output;
35752
+ }
35753
+ const formatted = messages.map((m) => {
35754
+ const marker17 = m.id === id ? ">>> " : " ";
35755
+ return `${marker17}[${m.id}] (${m.type})
35756
+ ${marker17}${m.content}`;
35757
+ }).join(`
35758
+
35759
+ `);
35760
+ results.set(toolCallId, { output: formatted, done: false });
35761
+ return formatted;
35762
+ }
35763
+ });
35764
+ tools.web_search = tool({
35765
+ description: WebSearchTool.definition.description,
35766
+ parameters: WebSearchTool.definition.parameters,
35767
+ execute: async (args, { toolCallId }) => {
35768
+ const toolResult = await WebSearchTool.definition.execute(args, createBaseContext(toolCallId));
35769
+ results.set(toolCallId, { output: toolResult.output, done: false });
35770
+ return toolResult.output;
35771
+ }
35772
+ });
35773
+ tools.web_fetch = tool({
35774
+ description: WebFetchTool.definition.description,
35775
+ parameters: WebFetchTool.definition.parameters,
35776
+ execute: async (args, { toolCallId }) => {
35777
+ const toolResult = await WebFetchTool.definition.execute(args, createBaseContext(toolCallId));
35778
+ results.set(toolCallId, { output: toolResult.output, done: false });
35779
+ return toolResult.output;
35780
+ }
35781
+ });
35782
+ tools.glob = tool({
35783
+ description: GlobTool.definition.description,
35784
+ parameters: GlobTool.definition.parameters,
35785
+ execute: async (args, { toolCallId }) => {
35786
+ const toolResult = await GlobTool.definition.execute(args, createBaseContext(toolCallId));
35787
+ results.set(toolCallId, { output: toolResult.output, done: false });
35788
+ return toolResult.output;
35789
+ }
35790
+ });
35791
+ tools.read = tool({
35792
+ description: ReadTool.definition.description,
35793
+ parameters: ReadTool.definition.parameters,
35794
+ execute: async (args, { toolCallId }) => {
35795
+ const toolResult = await ReadTool.definition.execute(args, createBaseContext(toolCallId));
35796
+ results.set(toolCallId, { output: toolResult.output, done: false });
35797
+ return toolResult.output;
35798
+ }
35799
+ });
35800
+ tools.grep = tool({
35801
+ description: GrepTool.definition.description,
35802
+ parameters: GrepTool.definition.parameters,
35803
+ execute: async (args, { toolCallId }) => {
35804
+ const toolResult = await GrepTool.definition.execute(args, createBaseContext(toolCallId));
35805
+ results.set(toolCallId, { output: toolResult.output, done: false });
35806
+ return toolResult.output;
35807
+ }
35808
+ });
35809
+ tools.finish_research = tool({
35810
+ description: "Complete the research and return your findings to the main agent.",
35811
+ parameters: exports_external.object({
35812
+ report: exports_external.string().describe("Your research report. Include: 1) Summary of what you learned, 2) LTM entries created/updated with [[slug]] references, 3) Key sources consulted.")
35813
+ }),
35814
+ execute: async ({ report }, { toolCallId }) => {
35815
+ results.set(toolCallId, { output: "Research complete.", done: true, report });
35816
+ return "Research complete.";
35817
+ }
35818
+ });
35819
+ return {
35820
+ tools,
35821
+ getLastResult: (toolCallId) => results.get(toolCallId)
35822
+ };
35823
+ }
35824
+
35825
+ // src/research/index.ts
35826
+ var MAX_RESEARCH_TURNS = 50;
35827
+ function buildResearchPrompt(topic) {
35828
+ return `## Research Mode
35829
+
35830
+ I am now in **research mode**. My task is to investigate a topic and build knowledge in my LTM.
35831
+
35832
+ **Topic to research:**
35833
+ ${topic}
35834
+
35835
+ ---
35836
+
35837
+ ### My Approach
35838
+
35839
+ 1. **Check existing knowledge** - What do I already know? (ltm_search, ltm_glob, ltm_read)
35840
+ 2. **Search conversation history** - Have we discussed this before? (search_messages, get_message)
35841
+ 3. **Research external sources** - What can I learn from the web? (web_search, web_fetch)
35842
+ 4. **Read relevant code** - If it's about our codebase, look at the source (glob, read, grep)
35843
+ 5. **Synthesize into LTM** - Create or update entries with what I learned (ltm_create, ltm_update)
35844
+ 6. **Return a report** - Summarize findings and what was added (finish_research)
35845
+
35846
+ ### Guidelines
35847
+
35848
+ - **Don't duplicate** - Search before creating. Update existing entries if relevant.
35849
+ - **Cross-link** - Use [[slug]] syntax to connect related entries.
35850
+ - **Cite sources** - Note where information came from (URLs, file paths, conversation IDs).
35851
+ - **Be thorough** - This is directed research, not a quick lookup. Take time to understand deeply.
35852
+ - **Focus on actionable knowledge** - What will help me work more effectively?
35853
+
35854
+ ### Tools Available
35855
+
35856
+ **LTM (full access):**
35857
+ - \`ltm_glob(pattern)\` - Browse tree structure
35858
+ - \`ltm_search(query)\` - Find entries by keyword
35859
+ - \`ltm_read(slug)\` - Read full entry
35860
+ - \`ltm_create(slug, parentSlug, title, body)\` - Create new entry
35861
+ - \`ltm_update(slug, body, version)\` - Replace entry content
35862
+ - \`ltm_edit(slug, old, new, version)\` - Surgical edit
35863
+ - \`ltm_reparent(slug, newParentSlug, version)\` - Move entry
35864
+ - \`ltm_archive(slug, version)\` - Remove outdated entry
35865
+
35866
+ **History:**
35867
+ - \`search_messages(query)\` - Full-text search in conversation history
35868
+ - \`get_message(id, contextBefore, contextAfter)\` - Get specific message with context
35869
+
35870
+ **Web:**
35871
+ - \`web_search(query)\` - Search the web
35872
+ - \`web_fetch(url, question)\` - Read a webpage and extract info
35873
+
35874
+ **Files:**
35875
+ - \`glob(pattern)\` - Find files matching pattern
35876
+ - \`read(filePath)\` - Read file contents
35877
+ - \`grep(pattern)\` - Search file contents
35878
+
35879
+ **Control:**
35880
+ - \`finish_research(report)\` - Complete research and return findings
35881
+
35882
+ ---
35883
+
35884
+ When done, call \`finish_research\` with a report that includes:
35885
+ 1. Summary of what I learned
35886
+ 2. What LTM entries I created or updated (with [[slug]] references)
35887
+ 3. Key sources consulted
35888
+
35889
+ Be thorough! This is research, not a quick answer.
35890
+ `;
35891
+ }
35892
+ async function runResearch(storage, topic) {
35893
+ activity.research?.start?.("Research", { topic: topic.slice(0, 50) });
35894
+ const result = {
35895
+ report: "",
35896
+ entriesCreated: [],
35897
+ entriesUpdated: [],
35898
+ turnsUsed: 0,
35899
+ usage: { inputTokens: 0, outputTokens: 0 }
35900
+ };
35901
+ const { tools, getLastResult } = buildResearchTools(storage);
35902
+ const subAgentResult = await runSubAgent(storage, {
35903
+ name: "research",
35904
+ taskPrompt: buildResearchPrompt(topic),
35905
+ tools,
35906
+ finishToolName: "finish_research",
35907
+ extractResult: () => {
35908
+ return result.report || null;
35909
+ },
35910
+ tier: "workhorse",
35911
+ maxTurns: MAX_RESEARCH_TURNS,
35912
+ maxTokens: 8192,
35913
+ onToolResult: (toolCallId) => {
35914
+ const toolResult = getLastResult(toolCallId);
35915
+ if (!toolResult)
35916
+ return;
35917
+ if (toolResult.entryCreated && toolResult.slug) {
35918
+ result.entriesCreated.push(toolResult.slug);
35919
+ }
35920
+ if (toolResult.entryUpdated && toolResult.slug) {
35921
+ result.entriesUpdated.push(toolResult.slug);
35922
+ }
35923
+ if (toolResult.report) {
35924
+ result.report = toolResult.report;
35925
+ }
35926
+ }
35927
+ });
35928
+ result.turnsUsed = subAgentResult.turnsUsed;
35929
+ result.usage = subAgentResult.usage;
35930
+ if (!result.report) {
35931
+ result.report = "Research ended without explicit report.";
35932
+ }
35933
+ activity.research?.complete?.(`${result.turnsUsed} turns, ${result.entriesCreated.length} created, ${result.entriesUpdated.length} updated`);
35934
+ return result;
35935
+ }
35936
+
35937
+ // src/tool/research.ts
35938
+ var DESCRIPTION4 = `Investigate a topic and build knowledge in your long-term memory.
35939
+
35940
+ This spawns a research sub-agent that can:
35941
+ - Search and update your long-term knowledge base
35942
+ - Search the web and fetch documentation
35943
+ - Search your conversation history
35944
+ - Read files in the codebase
35945
+
35946
+ Use this when you need to:
35947
+ - Deeply understand something before implementing
35948
+ - Document a service, API, or system
35949
+ - Build a profile of a company or technology
35950
+ - Research best practices or prior art
35951
+
35952
+ The sub-agent will research thoroughly and return a report of what it learned and what LTM entries it created or updated.`;
35953
+ var parameters4 = exports_external.object({
35954
+ topic: exports_external.string().describe("The topic to research. Be specific about what you want to learn. Examples: 'How does Stripe's payment intent API work?', 'Document the authentication flow in our codebase', 'Research best practices for rate limiting'")
35955
+ });
35956
+ var ResearchTool = Tool.define("research", {
35957
+ description: DESCRIPTION4,
35958
+ parameters: parameters4,
35959
+ async execute({ topic }, ctx) {
35960
+ const storage = ctx.extra?.storage;
35961
+ if (!storage) {
35962
+ return {
35963
+ output: "Error: Storage not available for research",
35964
+ title: "Research failed",
35965
+ metadata: {
35966
+ topic,
35967
+ turnsUsed: 0,
35968
+ entriesCreated: [],
35969
+ entriesUpdated: [],
35970
+ inputTokens: 0,
35971
+ outputTokens: 0
35972
+ }
35973
+ };
35974
+ }
35975
+ try {
35976
+ const result = await runResearch(storage, topic);
35977
+ return {
35978
+ output: result.report,
35979
+ title: `Researched: ${topic.slice(0, 40)}${topic.length > 40 ? "..." : ""}`,
35980
+ metadata: {
35981
+ topic,
35982
+ turnsUsed: result.turnsUsed,
35983
+ entriesCreated: result.entriesCreated,
35984
+ entriesUpdated: result.entriesUpdated,
35985
+ inputTokens: result.usage.inputTokens,
35986
+ outputTokens: result.usage.outputTokens
35987
+ }
35988
+ };
35989
+ } catch (error) {
35990
+ const errorMsg = error instanceof Error ? error.message : String(error);
35991
+ return {
35992
+ output: `Research failed: ${errorMsg}`,
35993
+ title: "Research error",
35994
+ metadata: {
35995
+ topic,
35996
+ turnsUsed: 0,
35997
+ entriesCreated: [],
35998
+ entriesUpdated: [],
35999
+ inputTokens: 0,
36000
+ outputTokens: 0
36001
+ }
36002
+ };
36003
+ }
36004
+ }
36005
+ });
35559
36006
  // node_modules/zod/v4/core/core.js
35560
36007
  var NEVER2 = Object.freeze({
35561
36008
  status: "aborted"
@@ -43913,7 +44360,7 @@ var Mcp;
43913
44360
  })(Mcp ||= {});
43914
44361
 
43915
44362
  // src/tool/mcp-status.ts
43916
- var parameters4 = exports_external.object({});
44363
+ var parameters5 = exports_external.object({});
43917
44364
  var McpStatusTool = Tool.define("mcp_status", {
43918
44365
  description: `Inspect MCP (Model Context Protocol) server status.
43919
44366
 
@@ -43926,7 +44373,7 @@ Use this when:
43926
44373
  - A tool you expect isn't available
43927
44374
  - You want to see what MCP capabilities are configured
43928
44375
  - Debugging MCP connection issues`,
43929
- parameters: parameters4,
44376
+ parameters: parameters5,
43930
44377
  async execute(_args, _ctx) {
43931
44378
  const status = Mcp.getStatus();
43932
44379
  const toolNames = Mcp.getToolNames();
@@ -44442,26 +44889,8 @@ var LTMTools = {
44442
44889
  ...LTMReadOnlyTools,
44443
44890
  ...LTMWriteTools
44444
44891
  };
44445
- // src/ltm/consolidation.ts
44446
- var log9 = Log.create({ service: "consolidation-agent" });
44447
- var MAX_CONSOLIDATION_TURNS = 20;
44448
- var AGENT_TYPE = "ltm-consolidate";
44449
- function isConversationNoteworthy(messages) {
44450
- if (messages.length < 5) {
44451
- return false;
44452
- }
44453
- let hasToolUsage = false;
44454
- let hasSubstantialContent = false;
44455
- for (const msg of messages) {
44456
- if (msg.type === "tool_call" || msg.type === "tool_result") {
44457
- hasToolUsage = true;
44458
- }
44459
- if (msg.content.length > 200) {
44460
- hasSubstantialContent = true;
44461
- }
44462
- }
44463
- return hasToolUsage || hasSubstantialContent;
44464
- }
44892
+ // src/ltm/tools.ts
44893
+ var AGENT_TYPE2 = "ltm-consolidate";
44465
44894
  function buildConsolidationTools(storage) {
44466
44895
  const results = new Map;
44467
44896
  const createLTMContext2 = (toolCallId) => {
@@ -44472,10 +44901,17 @@ function buildConsolidationTools(storage) {
44472
44901
  });
44473
44902
  ctx.extra = {
44474
44903
  ltm: storage.ltm,
44475
- agentType: AGENT_TYPE
44904
+ agentType: AGENT_TYPE2
44476
44905
  };
44477
44906
  return ctx;
44478
44907
  };
44908
+ const createBaseContext = (toolCallId) => {
44909
+ return Tool.createContext({
44910
+ sessionID: "consolidation",
44911
+ messageID: "consolidation",
44912
+ callID: toolCallId
44913
+ });
44914
+ };
44479
44915
  const tools = {};
44480
44916
  tools.ltm_read = tool({
44481
44917
  description: LTMReadTool.definition.description,
@@ -44628,12 +45064,7 @@ function buildConsolidationTools(storage) {
44628
45064
  description: ReadTool.definition.description,
44629
45065
  parameters: ReadTool.definition.parameters,
44630
45066
  execute: async (args, { toolCallId }) => {
44631
- const ctx = Tool.createContext({
44632
- sessionID: "consolidation",
44633
- messageID: "consolidation",
44634
- callID: toolCallId
44635
- });
44636
- const toolResult = await ReadTool.definition.execute(args, ctx);
45067
+ const toolResult = await ReadTool.definition.execute(args, createBaseContext(toolCallId));
44637
45068
  const result = { output: toolResult.output, done: false };
44638
45069
  results.set(toolCallId, result);
44639
45070
  return result.output;
@@ -44643,12 +45074,7 @@ function buildConsolidationTools(storage) {
44643
45074
  description: GlobTool.definition.description,
44644
45075
  parameters: GlobTool.definition.parameters,
44645
45076
  execute: async (args, { toolCallId }) => {
44646
- const ctx = Tool.createContext({
44647
- sessionID: "consolidation",
44648
- messageID: "consolidation",
44649
- callID: toolCallId
44650
- });
44651
- const toolResult = await GlobTool.definition.execute(args, ctx);
45077
+ const toolResult = await GlobTool.definition.execute(args, createBaseContext(toolCallId));
44652
45078
  const result = { output: toolResult.output, done: false };
44653
45079
  results.set(toolCallId, result);
44654
45080
  return result.output;
@@ -44658,12 +45084,7 @@ function buildConsolidationTools(storage) {
44658
45084
  description: GrepTool.definition.description,
44659
45085
  parameters: GrepTool.definition.parameters,
44660
45086
  execute: async (args, { toolCallId }) => {
44661
- const ctx = Tool.createContext({
44662
- sessionID: "consolidation",
44663
- messageID: "consolidation",
44664
- callID: toolCallId
44665
- });
44666
- const toolResult = await GrepTool.definition.execute(args, ctx);
45087
+ const toolResult = await GrepTool.definition.execute(args, createBaseContext(toolCallId));
44667
45088
  const result = { output: toolResult.output, done: false };
44668
45089
  results.set(toolCallId, result);
44669
45090
  return result.output;
@@ -44673,12 +45094,7 @@ function buildConsolidationTools(storage) {
44673
45094
  description: WebSearchTool.definition.description,
44674
45095
  parameters: WebSearchTool.definition.parameters,
44675
45096
  execute: async (args, { toolCallId }) => {
44676
- const ctx = Tool.createContext({
44677
- sessionID: "consolidation",
44678
- messageID: "consolidation",
44679
- callID: toolCallId
44680
- });
44681
- const toolResult = await WebSearchTool.definition.execute(args, ctx);
45097
+ const toolResult = await WebSearchTool.definition.execute(args, createBaseContext(toolCallId));
44682
45098
  const result = { output: toolResult.output, done: false };
44683
45099
  results.set(toolCallId, result);
44684
45100
  return result.output;
@@ -44688,12 +45104,7 @@ function buildConsolidationTools(storage) {
44688
45104
  description: WebFetchTool.definition.description,
44689
45105
  parameters: WebFetchTool.definition.parameters,
44690
45106
  execute: async (args, { toolCallId }) => {
44691
- const ctx = Tool.createContext({
44692
- sessionID: "consolidation",
44693
- messageID: "consolidation",
44694
- callID: toolCallId
44695
- });
44696
- const toolResult = await WebFetchTool.definition.execute(args, ctx);
45107
+ const toolResult = await WebFetchTool.definition.execute(args, createBaseContext(toolCallId));
44697
45108
  const result = { output: toolResult.output, done: false };
44698
45109
  results.set(toolCallId, result);
44699
45110
  return result.output;
@@ -44715,7 +45126,27 @@ function buildConsolidationTools(storage) {
44715
45126
  getLastResult: (toolCallId) => results.get(toolCallId)
44716
45127
  };
44717
45128
  }
44718
- async function buildLTMReviewTurn(storage, recentlyUpdatedEntries) {
45129
+
45130
+ // src/ltm/consolidation.ts
45131
+ var log9 = Log.create({ service: "consolidation-agent" });
45132
+ var MAX_CONSOLIDATION_TURNS = 20;
45133
+ function isConversationNoteworthy(messages) {
45134
+ if (messages.length < 5) {
45135
+ return false;
45136
+ }
45137
+ let hasToolUsage = false;
45138
+ let hasSubstantialContent = false;
45139
+ for (const msg of messages) {
45140
+ if (msg.type === "tool_call" || msg.type === "tool_result") {
45141
+ hasToolUsage = true;
45142
+ }
45143
+ if (msg.content.length > 200) {
45144
+ hasSubstantialContent = true;
45145
+ }
45146
+ }
45147
+ return hasToolUsage || hasSubstantialContent;
45148
+ }
45149
+ async function buildLTMReviewPrompt(storage, recentlyUpdatedEntries) {
44719
45150
  const allEntries = await storage.ltm.glob("/**");
44720
45151
  const treeView = renderCompactTree(allEntries, 3);
44721
45152
  let content = `## Knowledge Base Curation Task
@@ -44772,46 +45203,6 @@ ${recentlyUpdatedEntries.map((e) => `- **${e.slug}**: ${e.title}`).join(`
44772
45203
  - \u2713 "Tool pattern: parameters must be defined BEFORE Tool.define() call due to initialization order"
44773
45204
  - \u2717 "The config module handles configuration" (too obvious, adds no value)
44774
45205
 
44775
- **When to document code structure:**
44776
- - Complex modules with non-obvious responsibilities
44777
- - Files that interact in subtle ways
44778
- - Patterns that took effort to understand
44779
- - Entry points and key abstractions
44780
-
44781
- ---
44782
-
44783
- ### Curation Tasks (do these!)
44784
-
44785
- **Organize the tree:**
44786
- - Group related entries under parent paths
44787
- - Create small "index" entries that just organize children (this is fine!)
44788
- - A flat list of 20 entries is hard to navigate - a tree is better
44789
-
44790
- Example structure:
44791
- /miriad-code <- index entry: "My codebase - architecture and patterns"
44792
- /protocol <- NDJSON protocol details
44793
- /memory-system <- temporal, LTM, distillation
44794
- /user <- index entry: "Working with Simen"
44795
- /preferences <- simplicity over compat, incremental refactoring
44796
- /integrations <- index entry: "External systems"
44797
- /mcp <- MCP protocol, config resolution
44798
-
44799
- **Cross-link for findability:**
44800
- - Use [[slug]] syntax to connect related entries
44801
- - Ask: "If I search for X, will I find this?"
44802
- - Index entries can list their children with [[links]]
44803
-
44804
- **Maintain quality:**
44805
- - Combine entries that overlap significantly
44806
- - Split entries that cover too many topics
44807
- - Archive entries that are outdated or wrong
44808
- - Update entries with new information
44809
-
44810
- **Enrich with research:**
44811
- - Use web_search/web_fetch to fact-check or fill gaps
44812
- - Look up documentation for libraries/APIs you reference
44813
- - Verify assumptions about external systems
44814
-
44815
45206
  ---
44816
45207
 
44817
45208
  ### Tools Available
@@ -44827,10 +45218,6 @@ Example structure:
44827
45218
  - \`ltm_rename(slug, newSlug, version)\` - Change entry slug
44828
45219
  - \`ltm_archive(slug, version)\` - Remove outdated entry
44829
45220
 
44830
- To create a tree:
44831
- 1. Create parent first: \`ltm_create("miriad-code", null, "My Codebase", "Index of codebase knowledge...")\`
44832
- 2. Create children with parentSlug: \`ltm_create("protocol", "miriad-code", "Protocol", "...")\`
44833
-
44834
45221
  **Codebase Research:**
44835
45222
  - \`read(filePath)\` - Read a file to verify/enrich knowledge
44836
45223
  - \`glob(pattern)\` - Find files matching pattern
@@ -44840,12 +45227,6 @@ To create a tree:
44840
45227
  - \`web_search(query)\` - Search the web for information
44841
45228
  - \`web_fetch(url, question)\` - Read a webpage and extract info
44842
45229
 
44843
- Use codebase tools to:
44844
- - Verify file paths in entries still exist
44845
- - Check if documented patterns are still accurate
44846
- - Explore related files to build richer documentation
44847
- - Confirm technical details before recording them
44848
-
44849
45230
  ---
44850
45231
 
44851
45232
  ### Special Entries: /identity and /behavior
@@ -44860,16 +45241,12 @@ This frames everything you do. It's how you understand yourself in relationship
44860
45241
  - Other agents or entities you interact with
44861
45242
  - Your own nature and capabilities
44862
45243
 
44863
- This is actionable because it helps you form lasting relationships and maintain continuity. Update it as your understanding deepens.
44864
-
44865
45244
  **/behavior** - How you operate
44866
45245
  - User preferences you've learned
44867
45246
  - Working patterns that work well
44868
45247
  - Guidelines for code quality, testing, commits
44869
45248
  - Communication style preferences
44870
45249
 
44871
- Review these entries periodically. Are they accurate? Complete? Do they reflect what you've learned?
44872
-
44873
45250
  ---
44874
45251
 
44875
45252
  ### Your Task
@@ -44881,12 +45258,11 @@ Review these entries periodically. Are they accurate? Complete? Do they reflect
44881
45258
 
44882
45259
  ### Writing Your Summary
44883
45260
 
44884
- When you call \`finish_consolidation\`, write a brief note to your future self explaining what you captured and why it matters. This is a dialog between your subconscious (the curator) and your conscious self (the main agent).
45261
+ When you call \`finish_consolidation\`, write a brief note to your future self explaining what you captured and why it matters.
44885
45262
 
44886
45263
  **Good summaries** explain the WHAT and WHY:
44887
45264
  - "Captured the insights on probabilistic filtering in [[bloom-filter-overview]]. Also archived the X system workarounds since it's been decommissioned."
44888
- - "Updated [[user-preferences]] with the new testing philosophy - run tests before committing. Created [[cast-integration]] to document the engine abstraction pattern."
44889
- - "Reorganized the codebase knowledge - moved protocol docs under [[miriad-code/protocol]] and cross-linked with [[memory-system]]."
45265
+ - "Updated [[user-preferences]] with the new testing philosophy - run tests before committing."
44890
45266
 
44891
45267
  **Avoid mechanical summaries**:
44892
45268
  - \u2717 "Created 2 entries, updated 1 entry"
@@ -44915,55 +45291,50 @@ async function runConsolidation(storage, messages) {
44915
45291
  }
44916
45292
  result.ran = true;
44917
45293
  log9.info("starting consolidation", { messageCount: messages.length });
44918
- const ctx = await buildAgentContext(storage);
44919
45294
  const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000).toISOString();
44920
45295
  const allEntries = await storage.ltm.glob("/**");
44921
45296
  const recentlyUpdated = allEntries.filter((e) => e.updatedAt > oneHourAgo && e.slug !== "identity" && e.slug !== "behavior");
44922
- const reviewTurnContent = await buildLTMReviewTurn(storage, recentlyUpdated);
44923
- const model = Provider.getModelForTier("workhorse");
45297
+ const taskPrompt = await buildLTMReviewPrompt(storage, recentlyUpdated);
44924
45298
  const { tools, getLastResult } = buildConsolidationTools(storage);
44925
- const initialMessages = [
44926
- ...ctx.historyTurns,
44927
- { role: "user", content: `[SYSTEM TASK]
44928
-
44929
- ${reviewTurnContent}` }
44930
- ];
44931
- const loopResult = await runAgentLoop({
44932
- model,
44933
- systemPrompt: ctx.systemPrompt,
44934
- initialMessages,
45299
+ const subAgentResult = await runSubAgent(storage, {
45300
+ name: "ltm-curator",
45301
+ taskPrompt,
44935
45302
  tools,
44936
- maxTokens: 2048,
44937
- temperature: 0,
45303
+ finishToolName: "finish_consolidation",
45304
+ extractResult: () => {
45305
+ return result.summary;
45306
+ },
45307
+ tier: "workhorse",
44938
45308
  maxTurns: MAX_CONSOLIDATION_TURNS,
44939
- isDone: stopOnTool("finish_consolidation"),
45309
+ maxTokens: 2048,
44940
45310
  onToolResult: (toolCallId) => {
44941
45311
  const toolResult = getLastResult(toolCallId);
44942
- if (toolResult?.entryCreated) {
45312
+ if (!toolResult)
45313
+ return;
45314
+ if (toolResult.entryCreated) {
44943
45315
  result.entriesCreated++;
44944
45316
  if (toolResult.slug) {
44945
45317
  result.details.push(`Created [[${toolResult.slug}]]${toolResult.title ? ` - ${toolResult.title}` : ""}`);
44946
45318
  }
44947
45319
  }
44948
- if (toolResult?.entryUpdated) {
45320
+ if (toolResult.entryUpdated) {
44949
45321
  result.entriesUpdated++;
44950
45322
  if (toolResult.slug) {
44951
45323
  result.details.push(`Updated [[${toolResult.slug}]]`);
44952
45324
  }
44953
45325
  }
44954
- if (toolResult?.entryArchived) {
45326
+ if (toolResult.entryArchived) {
44955
45327
  result.entriesArchived++;
44956
45328
  if (toolResult.slug) {
44957
45329
  result.details.push(`Archived [[${toolResult.slug}]]`);
44958
45330
  }
44959
45331
  }
44960
- if (toolResult?.summary) {
45332
+ if (toolResult.summary) {
44961
45333
  result.summary = toolResult.summary;
44962
45334
  }
44963
45335
  }
44964
45336
  });
44965
- result.usage.inputTokens += loopResult.usage.inputTokens;
44966
- result.usage.outputTokens += loopResult.usage.outputTokens;
45337
+ result.usage = subAgentResult.usage;
44967
45338
  if (!result.summary) {
44968
45339
  result.summary = "Consolidation ended without explicit finish";
44969
45340
  }
@@ -45252,6 +45623,16 @@ function createToolContextFactory(storage, sessionId, messageId, abortSignal) {
45252
45623
  storage
45253
45624
  };
45254
45625
  return ctx;
45626
+ },
45627
+ createResearchContext(callId) {
45628
+ const ctx = Tool.createContext({
45629
+ ...baseContext,
45630
+ callID: callId
45631
+ });
45632
+ ctx.extra = {
45633
+ storage
45634
+ };
45635
+ return ctx;
45255
45636
  }
45256
45637
  };
45257
45638
  }
@@ -45369,6 +45750,11 @@ function buildTools(storage, sessionId, messageId, abortSignal) {
45369
45750
  parameters: ReflectTool.definition.parameters,
45370
45751
  execute: async (args, { toolCallId }) => safeExecute("reflect", () => ReflectTool.definition.execute(args, factory.createReflectContext(toolCallId)))
45371
45752
  });
45753
+ tools.research = tool({
45754
+ description: ResearchTool.definition.description,
45755
+ parameters: ResearchTool.definition.parameters,
45756
+ execute: async (args, { toolCallId }) => safeExecute("research", () => ResearchTool.definition.execute(args, factory.createResearchContext(toolCallId)))
45757
+ });
45372
45758
  return tools;
45373
45759
  }
45374
45760
  async function initializeMcp() {
@@ -46805,8 +47191,8 @@ async function runRepl(options) {
46805
47191
  }
46806
47192
 
46807
47193
  // src/version.ts
46808
- var VERSION = "0.1.12";
46809
- var GIT_HASH = "712107d";
47194
+ var VERSION = "0.1.13";
47195
+ var GIT_HASH = "1d9eea4";
46810
47196
  var VERSION_STRING = `miriad-code v${VERSION} (${GIT_HASH})`;
46811
47197
 
46812
47198
  // src/cli/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miriad-systems/nuum",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
4
4
  "description": "AI coding agent with continuous memory - infinite context across sessions",
5
5
  "type": "module",
6
6
  "bin": {