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
|
@@ -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
|
|
2020
|
-
|
|
2052
|
+
function evolutionSuggest(args = {}) {
|
|
2053
|
+
const project = args.project || projectName()
|
|
2021
2054
|
const now = Date.now()
|
|
2022
|
-
const
|
|
2023
|
-
const
|
|
2024
|
-
const
|
|
2025
|
-
const
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
const
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
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
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
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
|
+
}
|
|
2050
2086
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
.
|
|
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
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
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
|
-
|
|
2095
|
-
|
|
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
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
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 }
|