mio-agent-runtime 0.5.3 → 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.3",
3
+ "version": "0.5.4",
4
4
  "description": "Mio Agent Runtime installer and MCP intelligence layer",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -20,7 +20,8 @@ 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
22
  const digestStatePath = path.join(dataDir, 'digest_state.json')
23
- const ideasPath = path.join(dataDir, 'ideas.jsonl')
23
+ const ideasPath = path.join(dataDir, 'ideas.jsonl')
24
+ const suggestionsPath = path.join(dataDir, 'suggestions.jsonl')
24
25
  fs.mkdirSync(dataDir, { recursive: true })
25
26
 
26
27
  const { loadPhase0, summarizePhase0, renderPhase0Markdown } = require('./phase0')
@@ -1649,9 +1650,39 @@ const TOOLS = [
1649
1650
  }
1650
1651
  },
1651
1652
  },
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
+ },
1653
1663
  ]
1654
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
+
1655
1686
  async function callTool(name, args = {}) {
1656
1687
  switch (name) {
1657
1688
  case 'mio.memory.query':
@@ -1696,6 +1727,8 @@ async function callTool(name, args = {}) {
1696
1727
  return handleIdeaGenerate(args)
1697
1728
  case 'mio.idea.list':
1698
1729
  return listIdeas(args)
1730
+ case 'mio.evolution.suggest':
1731
+ return evolutionSuggest(args)
1699
1732
  default:
1700
1733
  throw new Error(`Unknown tool: ${name}`)
1701
1734
  }
@@ -2016,91 +2049,64 @@ function recordTaskOutcome(args = {}) {
2016
2049
  }
2017
2050
 
2018
2051
 
2019
- function listIdeas(args = {}) {
2020
- if (!fs.existsSync(ideasPath)) return { ideas: [], count: 0, total: 0 }
2052
+ function evolutionSuggest(args = {}) {
2053
+ const project = args.project || projectName()
2021
2054
  const now = Date.now()
2022
- const period = String(args.period || 'all').trim()
2023
- const periodMs = period === '24h' ? 86400000 : period === '7d' ? 604800000 : period === '30d' ? 2592000000 : Infinity
2024
- const cutoff = periodMs === Infinity ? 0 : now - periodMs
2025
- const goalFilter = args.goal ? String(args.goal).toLowerCase() : null
2026
- const strategyFilter = args.strategy ? String(args.strategy).trim().toLowerCase() : null
2027
- const limit = Math.min(Math.max(Number(args.limit) || 10, 1), 50)
2028
- const all = fs.readFileSync(ideasPath, 'utf8').split('\n').filter(Boolean).map(line => {
2029
- try { return JSON.parse(line) } catch (_) { return null }
2030
- }).filter(Boolean)
2031
- const filtered = all.filter(idea => {
2032
- if (cutoff && new Date(idea.generatedAt).getTime() < cutoff) return false
2033
- if (goalFilter && !String(idea.goal || '').toLowerCase().includes(goalFilter)) return false
2034
- if (strategyFilter && String(idea.provenance?.strategy || '').toLowerCase() !== strategyFilter) return false
2035
- return true
2036
- })
2037
- return { ideas: filtered.slice(-limit), count: filtered.length, total: all.length }
2038
- }
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 = []
2039
2073
 
2040
- async function handleIdeaGenerate(args = {}) {
2041
- const { goal, context = '', constraints = [], numIdeas = 3 } = args
2042
- // Memory-grounding: reuse related prior decisions/problems from Mio so ideas
2043
- // are informed by the Agent Control Plane's accumulated experience.
2044
- let related = []
2045
- try {
2046
- const mem = await queryMemory({ query: goal, limit: 3 })
2047
- if (mem && Array.isArray(mem.results)) related = mem.results
2048
- } catch (e) {
2049
- related = []
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
+ }
2050
2086
  }
2051
- const relatedIds = related.map((r) => r.id).filter(Boolean)
2052
- const grounding = related
2053
- .filter((r) => r && (r.content || r.kind))
2054
- .map((r) => '- [' + (r.kind || 'note') + '] ' + String(r.content || '').slice(0, 240))
2055
- .join('\n')
2056
-
2057
- // Generation strategies give diversity without exposing the mechanism to callers.
2058
- const strategies = [
2059
- 'SCAMPER',
2060
- 'Analogy',
2061
- 'First-Principles',
2062
- 'Random-Stimulus',
2063
- 'Constraint-Inversion'
2064
- ]
2065
-
2066
- const ideas = []
2067
- for (let i = 0; i < numIdeas; i++) {
2068
- const strategy = strategies[i % strategies.length]
2069
- const blocks = [
2070
- 'Goal: ' + goal,
2071
- 'Strategy: ' + strategy,
2072
- context ? 'Context: ' + context : '',
2073
- constraints.length ? 'Constraints: ' + constraints.join(', ') : '',
2074
- related.length
2075
- ? 'Related Mio memory:\n' + grounding
2076
- : 'No related Mio memory found yet; record outcomes to grow the experience base.',
2077
- 'Suggestion: Consider prototyping the idea and measuring its impact against prior decisions.'
2078
- ].filter(Boolean)
2079
- ideas.push({
2080
- id: 'idea_' + Date.now() + '_' + i,
2081
- title: 'Idea #' + (i + 1) + ' (' + strategy + '): ' + goal,
2082
- description: blocks.join('\n\n'),
2083
- provenance: {
2084
- strategy: strategy,
2085
- relatedMemoryIds: relatedIds
2086
- }
2087
- })
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' })
2088
2092
  }
2089
- for (const idea of ideas) {
2090
- try {
2091
- fs.appendFileSync(ideasPath, JSON.stringify({ id: idea.id, title: idea.title, description: idea.description, provenance: idea.provenance, goal, context: context || null, constraints, generatedAt: idea.generatedAt }) + '\n')
2092
- } catch (_) {}
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' })
2093
2096
  }
2094
- for (const idea of ideas) {
2095
- try {
2096
- fs.appendFileSync(ideasPath, JSON.stringify({ id: idea.id, title: idea.title, description: idea.description, provenance: idea.provenance, goal, context: context || null, constraints, generatedAt: idea.generatedAt }) + '\n')
2097
- } catch (_) {}
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' })
2098
2099
  }
2099
- return {
2100
- ideas: ideas,
2101
- generatedAt: new Date().toISOString(),
2102
- 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
+ }
2103
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
2104
2110
  }
2105
2111
 
2106
2112
  module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }