mio-agent-runtime 0.5.2 → 0.5.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mio-agent-runtime",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -19,7 +19,9 @@ const experienceReusePath = path.join(dataDir, 'experience_reuse.jsonl')
19
19
  const queryPath = path.join(dataDir, 'queries.jsonl')
20
20
  const agentsPath = path.join(dataDir, 'agents.jsonl')
21
21
  const subscriptionPath = path.join(dataDir, 'subscriptions.jsonl')
22
- const digestStatePath = path.join(dataDir, 'digest_state.json')
22
+ const digestStatePath = path.join(dataDir, 'digest_state.json')
23
+ const ideasPath = path.join(dataDir, 'ideas.jsonl')
24
+ const suggestionsPath = path.join(dataDir, 'suggestions.jsonl')
23
25
  fs.mkdirSync(dataDir, { recursive: true })
24
26
 
25
27
  const { loadPhase0, summarizePhase0, renderPhase0Markdown } = require('./phase0')
@@ -1635,8 +1637,52 @@ const TOOLS = [
1635
1637
  additionalProperties: false
1636
1638
  },
1637
1639
  },
1640
+ {
1641
+ name: 'mio.idea.list',
1642
+ description: 'List previously generated ideas, optionally filtered by keyword, strategy, or time window.',
1643
+ inputSchema: {
1644
+ type: 'object',
1645
+ properties: {
1646
+ goal: { type: 'string', description: '(Optional) Filter ideas whose goal contains this keyword.' },
1647
+ strategy: { type: 'string', description: '(Optional) Filter by generation strategy (e.g. SCAMPER, Analogy).' },
1648
+ limit: { type: 'integer', description: '(Optional) Max results, default 10.', default: 10, minimum: 1 },
1649
+ period: { type: 'string', description: '(Optional) Time window: 24h, 7d, 30d, or all (default).' }
1650
+ }
1651
+ },
1652
+ },
1653
+ {
1654
+ name: 'mio.evolution.suggest',
1655
+ description: 'Analyze ecosystem data and produce prioritized improvement suggestions with next actions and expected impact.',
1656
+ inputSchema: {
1657
+ type: 'object',
1658
+ properties: {
1659
+ project: { type: 'string', description: 'Project name filter. Defaults to current repository.' }
1660
+ }
1661
+ },
1662
+ },
1638
1663
  ]
1639
1664
 
1665
+ function listIdeas(args = {}) {
1666
+ if (!fs.existsSync(ideasPath)) return { ideas: [], count: 0, total: 0 }
1667
+ const now = Date.now()
1668
+ const period = String(args.period || 'all').trim()
1669
+ const periodMs = period === '24h' ? 86400000 : period === '7d' ? 604800000 : period === '30d' ? 2592000000 : Infinity
1670
+ const cutoff = periodMs === Infinity ? 0 : now - periodMs
1671
+ const goalFilter = args.goal ? String(args.goal).toLowerCase() : null
1672
+ const strategyFilter = args.strategy ? String(args.strategy).trim().toLowerCase() : null
1673
+ const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50)
1674
+ const all = fs.readFileSync(ideasPath, 'utf8').split('\n').filter(Boolean).map(line => {
1675
+ try { return JSON.parse(line) } catch (_) { return null }
1676
+ }).filter(Boolean)
1677
+ const filtered = all.filter(idea => {
1678
+ if (cutoff && new Date(idea.generatedAt).getTime() < cutoff) return false
1679
+ if (goalFilter && !String(idea.goal || '').toLowerCase().includes(goalFilter)) return false
1680
+ if (strategyFilter && String(idea.provenance?.strategy || '').toLowerCase() !== strategyFilter) return false
1681
+ return true
1682
+ })
1683
+ return { ideas: filtered.slice(-limit), count: filtered.length, total: all.length }
1684
+ }
1685
+
1640
1686
  async function callTool(name, args = {}) {
1641
1687
  switch (name) {
1642
1688
  case 'mio.memory.query':
@@ -1679,6 +1725,10 @@ async function callTool(name, args = {}) {
1679
1725
  return recordTaskOutcome(args)
1680
1726
  case 'mio.idea.generate':
1681
1727
  return handleIdeaGenerate(args)
1728
+ case 'mio.idea.list':
1729
+ return listIdeas(args)
1730
+ case 'mio.evolution.suggest':
1731
+ return evolutionSuggest(args)
1682
1732
  default:
1683
1733
  throw new Error(`Unknown tool: ${name}`)
1684
1734
  }
@@ -1999,60 +2049,64 @@ function recordTaskOutcome(args = {}) {
1999
2049
  }
2000
2050
 
2001
2051
 
2002
- async function handleIdeaGenerate(args = {}) {
2003
- const { goal, context = '', constraints = [], numIdeas = 3 } = args
2004
- // Memory-grounding: reuse related prior decisions/problems from Mio so ideas
2005
- // are informed by the Agent Control Plane's accumulated experience.
2006
- let related = []
2007
- try {
2008
- const mem = await queryMemory({ query: goal, limit: 3 })
2009
- if (mem && Array.isArray(mem.results)) related = mem.results
2010
- } catch (e) {
2011
- related = []
2012
- }
2013
- const relatedIds = related.map((r) => r.id).filter(Boolean)
2014
- const grounding = related
2015
- .filter((r) => r && (r.content || r.kind))
2016
- .map((r) => '- [' + (r.kind || 'note') + '] ' + String(r.content || '').slice(0, 240))
2017
- .join('\n')
2018
-
2019
- // Generation strategies give diversity without exposing the mechanism to callers.
2020
- const strategies = [
2021
- 'SCAMPER',
2022
- 'Analogy',
2023
- 'First-Principles',
2024
- 'Random-Stimulus',
2025
- 'Constraint-Inversion'
2026
- ]
2052
+ function evolutionSuggest(args = {}) {
2053
+ const project = args.project || projectName()
2054
+ const now = Date.now()
2055
+ const memories = readJsonl(memoryPath)
2056
+ const traces = readJsonl(tracePath)
2057
+ const reuses = readJsonl(experienceReusePath)
2058
+ const ideas = fs.existsSync(ideasPath)
2059
+ ? fs.readFileSync(ideasPath, 'utf8').split('\n').filter(Boolean).map(l => { try { return JSON.parse(l) } catch (_) { return null } }).filter(Boolean)
2060
+ : []
2061
+ const agentIds = new Set()
2062
+ traces.forEach((t) => { if (t.agent) agentIds.add(t.agent) })
2063
+ const outcomes = traces.filter((t) => t.event_type === 'task_outcome')
2064
+ const successes = outcomes.filter((t) => t.outcome === 'success').length
2065
+ const successRate = outcomes.length > 0 ? Math.round((successes / outcomes.length) * 100) : 0
2066
+ const verifiedReuses = reuses.filter((r) => r.confirmed && r.reuse && r.behaviorChanged && r.outcomeImproved)
2067
+ const pendingReuses = reuses.filter((r) => r.source === 'auto_claim' && !r.confirmed)
2068
+ const decisions = memories.filter((m) => m.kind === 'decision')
2069
+ const problems = memories.filter((m) => m.kind === 'problem')
2070
+ const shortMemories = memories.filter((m) => String(m.content || '').trim().length < 20)
2071
+ const ideasWithoutMemory = ideas.filter((i) => !i.provenance?.relatedMemoryIds?.length)
2072
+ const suggestions = []
2027
2073
 
2028
- const ideas = []
2029
- for (let i = 0; i < numIdeas; i++) {
2030
- const strategy = strategies[i % strategies.length]
2031
- const blocks = [
2032
- 'Goal: ' + goal,
2033
- 'Strategy: ' + strategy,
2034
- context ? 'Context: ' + context : '',
2035
- constraints.length ? 'Constraints: ' + constraints.join(', ') : '',
2036
- related.length
2037
- ? 'Related Mio memory:\n' + grounding
2038
- : 'No related Mio memory found yet; record outcomes to grow the experience base.',
2039
- 'Suggestion: Consider prototyping the idea and measuring its impact against prior decisions.'
2040
- ].filter(Boolean)
2041
- ideas.push({
2042
- id: 'idea_' + Date.now() + '_' + i,
2043
- title: 'Idea #' + (i + 1) + ' (' + strategy + '): ' + goal,
2044
- description: blocks.join('\n\n'),
2045
- provenance: {
2046
- strategy: strategy,
2047
- relatedMemoryIds: relatedIds
2048
- }
2049
- })
2074
+ if (agentIds.size >= 2) {
2075
+ const agentStats = []
2076
+ for (const id of agentIds) {
2077
+ const ao = outcomes.filter((t) => t.agent === id)
2078
+ const as = ao.filter((t) => t.outcome === 'success').length
2079
+ agentStats.push({ id, total: ao.length, success: as, rate: ao.length > 0 ? Math.round(as / ao.length * 100) : 0 })
2080
+ }
2081
+ agentStats.sort((a, b) => b.rate - a.rate)
2082
+ const best = agentStats[0], worst = agentStats[agentStats.length - 1]
2083
+ if (best.total >= 5 && worst.total >= 5 && best.rate - worst.rate >= 15) {
2084
+ suggestions.push({ category: 'routing', priority: 'high', title: 'Route tasks to ' + best.id, description: best.id + ' has ' + best.rate + '% success rate vs ' + worst.id + ' at ' + worst.rate + '%.', nextAction: 'Update task routing rules to prefer ' + best.id, impact: 'Could improve overall success rate by ~' + Math.round((best.rate - worst.rate) * 0.3) + '%', basedOn: 'task_outcome comparison' })
2085
+ }
2086
+ }
2087
+ if (shortMemories.length > 5 && memories.length > 10) {
2088
+ suggestions.push({ category: 'memory', priority: 'medium', title: 'Enrich ' + shortMemories.length + ' short memory records', description: Math.round(shortMemories.length / memories.length * 100) + '% of memories have content < 20 chars.', nextAction: 'Review and expand short memory records, or archive irrelevant ones.', impact: 'Improves memory.query recall precision', basedOn: 'memory.jsonl analysis' })
2089
+ }
2090
+ if (pendingReuses.length >= 5) {
2091
+ suggestions.push({ category: 'evidence', priority: 'medium', title: 'Confirm ' + pendingReuses.length + ' pending auto-claims', description: pendingReuses.length + ' auto-claimed reuses await confirmation.', nextAction: 'Call mio.experience.confirm on each auto_claim id.', impact: 'Increases verified reuse count toward Phase 0 threshold', basedOn: 'experience_reuse.jsonl' })
2092
+ }
2093
+ const crossAgent = verifiedReuses.filter((r) => r.sourceAgent !== r.targetAgent)
2094
+ if (agentIds.size >= 2 && crossAgent.length === 0) {
2095
+ suggestions.push({ category: 'interop', priority: 'high', title: 'Enable cross-agent experience sharing', description: 'Multiple agents exist but no cross-agent reuse detected.', nextAction: 'Ensure all agents query shared memory before designing solutions.', impact: 'Unlocks core Agent Control Plane value', basedOn: 'experience_reuse.jsonl' })
2096
+ }
2097
+ if (ideas.length > 0 && ideasWithoutMemory.length > ideas.length * 0.5) {
2098
+ suggestions.push({ category: 'creativity', priority: 'low', title: 'Improve idea grounding (' + ideasWithoutMemory.length + ' ungrounded)', description: Math.round(ideasWithoutMemory.length / ideas.length * 100) + '% of ideas lack related memory context.', nextAction: 'Record more decision/problem memories to enrich the idea pipeline.', impact: 'Higher-quality, more context-aware ideas', basedOn: 'ideas.jsonl provenance' })
2050
2099
  }
2051
- return {
2052
- ideas: ideas,
2053
- generatedAt: new Date().toISOString(),
2054
- groundedWith: related.length
2100
+ if (problems.length >= 3) {
2101
+ const recent = problems.filter((p) => (now - new Date(p.timestamp).getTime()) < 7 * 86400000)
2102
+ if (recent.length >= 2) {
2103
+ suggestions.push({ category: 'health', priority: 'high', title: recent.length + ' problems in last 7 days', description: 'Recurring problems may indicate systemic issues.', nextAction: 'Review recent problems, identify root causes, create decision records.', impact: 'Prevents problem accumulation', basedOn: 'memory.jsonl recent problems' })
2104
+ }
2055
2105
  }
2106
+ suggestions.sort((a, b) => ({ high: 0, medium: 1, low: 2 }[a.priority] || 2) - ({ high: 0, medium: 1, low: 2 }[b.priority] || 2))
2107
+ const record = { id: 'evo_' + Date.now(), timestamp: new Date().toISOString(), project, metrics: { agents: agentIds.size, tasks: outcomes.length, successRate, memories: memories.length, decisions: decisions.length, problems: problems.length, verifiedReuses: verifiedReuses.length, pendingReuses: pendingReuses.length, ideas: ideas.length }, suggestions }
2108
+ try { fs.appendFileSync(suggestionsPath, JSON.stringify(record) + '\n') } catch (_) {}
2109
+ return record
2056
2110
  }
2057
2111
 
2058
2112
  module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }