mio-agent-runtime 0.5.15 → 0.5.17
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/README.md +144 -236
- package/adapters/codex.js +122 -134
- package/adapters/hermes.js +122 -140
- package/adapters/host-utils.js +61 -61
- package/adapters/opencode.js +176 -215
- package/adapters/workbuddy.js +196 -242
- package/bin/mio.js +423 -424
- package/observe/observer.js +1326 -1335
- package/package.json +38 -25
- package/scripts/verify-packed-runtime.js +371 -0
- package/server/creativity-engine.js +397 -0
- package/server/evolution-cutover.js +119 -0
- package/server/host-capabilities.js +50 -0
- package/server/mio-intelligence-mcp/README.md +184 -173
- package/server/mio-intelligence-mcp/index.js +621 -300
- package/server/mio-intelligence-mcp/package.json +17 -17
- package/server/mio-intelligence-mcp/phase0.js +381 -381
- package/server/runtime-modules.js +111 -0
- package/server/mio-intelligence-mcp/dashboard-server.js +0 -171
|
@@ -5,11 +5,79 @@ const fs = require('fs')
|
|
|
5
5
|
const path = require('path')
|
|
6
6
|
const crypto = require('crypto')
|
|
7
7
|
const readline = require('readline')
|
|
8
|
-
const { execFileSync } = require('child_process')
|
|
8
|
+
const { execFileSync } = require('child_process')
|
|
9
|
+
const { getEvolutionStatus } = require('../runtime-modules.js')
|
|
10
|
+
const { listHostCapabilities } = require('../host-capabilities.js')
|
|
11
|
+
const { createEvolutionCutoverTools } = require('../evolution-cutover.js')
|
|
12
|
+
|
|
13
|
+
const { CreativityEngine } = require('../creativity-engine.js')
|
|
14
|
+
|
|
15
|
+
// Observer research pipeline (optional — installed via @akemi-mio/observer)
|
|
16
|
+
let ObserverStore = null
|
|
17
|
+
let ObserverService = null
|
|
18
|
+
try {
|
|
19
|
+
const obs = require('@akemi-mio/observer')
|
|
20
|
+
ObserverStore = obs.ObserverStore
|
|
21
|
+
ObserverService = obs.ObserverService
|
|
22
|
+
} catch {}
|
|
23
|
+
|
|
24
|
+
// Insight self-observation engine (optional — installed via @akemi-mio/insight)
|
|
25
|
+
let InsightStore = null
|
|
26
|
+
let InsightGenerator = null
|
|
27
|
+
let PresenceService = null
|
|
28
|
+
try {
|
|
29
|
+
const insight = require('@akemi-mio/insight')
|
|
30
|
+
InsightStore = insight.InsightStore
|
|
31
|
+
InsightGenerator = insight.InsightGenerator
|
|
32
|
+
PresenceService = insight.PresenceService
|
|
33
|
+
} catch {}
|
|
9
34
|
|
|
10
35
|
const SERVER_INFO = { name: 'mio-intelligence-mcp', version: '0.1.0' }
|
|
11
36
|
const PROTOCOL_VERSION = '2024-11-05'
|
|
12
37
|
|
|
38
|
+
// ═══════════════════════════════════════════════
|
|
39
|
+
// LLM chatJson — used by creativity engine
|
|
40
|
+
// ═══════════════════════════════════════════════
|
|
41
|
+
|
|
42
|
+
async function chatJson(userText, opts = {}) {
|
|
43
|
+
const apiUrl = process.env.LLM_API_URL || 'https://opencode.ai/zen/go/v1/chat/completions'
|
|
44
|
+
const apiKey = process.env.LLM_KEY || ''
|
|
45
|
+
const model = process.env.LLM_CHAT_MODEL || process.env.LLM_MODEL || 'deepseek-v4-flash'
|
|
46
|
+
const system = opts.system || 'You are a creative AI assistant. Output JSON.'
|
|
47
|
+
const temperature = opts.temperature ?? 0.3
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const resp = await fetch(apiUrl, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
55
|
+
},
|
|
56
|
+
body: JSON.stringify({
|
|
57
|
+
model,
|
|
58
|
+
temperature,
|
|
59
|
+
messages: [
|
|
60
|
+
{ role: 'system', content: system },
|
|
61
|
+
{ role: 'user', content: userText },
|
|
62
|
+
],
|
|
63
|
+
response_format: { type: 'json_object' },
|
|
64
|
+
}),
|
|
65
|
+
signal: AbortSignal.timeout(opts.timeoutMs || 30000),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (!resp.ok) return { error: `HTTP ${resp.status}` }
|
|
69
|
+
const json = await resp.json()
|
|
70
|
+
const text = json.choices?.[0]?.message?.content || ''
|
|
71
|
+
try {
|
|
72
|
+
return { data: JSON.parse(text) }
|
|
73
|
+
} catch {
|
|
74
|
+
return { data: text }
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return { error: String(err) }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
13
81
|
const dataDir = path.resolve(
|
|
14
82
|
process.env.MIO_DATA_DIR || path.join(process.cwd(), '.mio-intelligence')
|
|
15
83
|
)
|
|
@@ -20,11 +88,12 @@ const queryPath = path.join(dataDir, 'queries.jsonl')
|
|
|
20
88
|
const agentsPath = path.join(dataDir, 'agents.jsonl')
|
|
21
89
|
const subscriptionPath = path.join(dataDir, 'subscriptions.jsonl')
|
|
22
90
|
const digestStatePath = path.join(dataDir, 'digest_state.json')
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
fs.mkdirSync(dataDir, { recursive: true })
|
|
26
|
-
|
|
91
|
+
fs.mkdirSync(dataDir, { recursive: true })
|
|
92
|
+
|
|
27
93
|
const { loadPhase0, summarizePhase0, renderPhase0Markdown } = require('./phase0')
|
|
94
|
+
const evolutionCutover = createEvolutionCutoverTools({ dataDir, appendJsonl, readJsonl, projectName })
|
|
95
|
+
const creativityEngine = new CreativityEngine(path.join(dataDir, 'creativity'), chatJson)
|
|
96
|
+
const insightStore = InsightStore ? new InsightStore(path.join(dataDir, 'insights', 'insights.json')) : null
|
|
28
97
|
|
|
29
98
|
let runtimeContext = {}
|
|
30
99
|
try {
|
|
@@ -1594,209 +1663,534 @@ const TOOLS = [
|
|
|
1594
1663
|
},
|
|
1595
1664
|
},
|
|
1596
1665
|
},
|
|
1597
|
-
{
|
|
1598
|
-
name: 'mio.evolution.report',
|
|
1599
|
-
description: 'Generate cross-agent evolution report: ecosystem summary, agent performance comparison, cross-agent patterns, memory health, and optimization suggestions (ADR-017 Evolution plane).',
|
|
1666
|
+
{
|
|
1667
|
+
name: 'mio.evolution.report',
|
|
1668
|
+
description: 'Generate cross-agent evolution report: ecosystem summary, agent performance comparison, cross-agent patterns, memory health, and optimization suggestions (ADR-017 Evolution plane).',
|
|
1600
1669
|
inputSchema: {
|
|
1601
1670
|
type: 'object',
|
|
1602
1671
|
properties: {
|
|
1603
1672
|
project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
|
|
1604
1673
|
period: { type: 'string', description: 'Time window: 24h, 7d, 30d, or all (default).' },
|
|
1605
1674
|
},
|
|
1606
|
-
},
|
|
1607
|
-
},
|
|
1608
|
-
{
|
|
1609
|
-
name: 'mio.task.record_outcome',
|
|
1610
|
-
description: 'Record task outcome with agent registry update and auto-claim experience reuse in one atomic call. Combines observer.ingest + agent stats + auto-claim for a complete task lifecycle closure.',
|
|
1611
|
-
inputSchema: {
|
|
1612
|
-
type: 'object',
|
|
1613
|
-
properties: {
|
|
1614
|
-
outcome: { type: 'string', description: 'Task outcome: success, failure, or aborted.' },
|
|
1615
|
-
task: { type: 'string', description: 'Brief task description.' },
|
|
1616
|
-
summary: { type: 'string', description: 'Optional task summary.' },
|
|
1617
|
-
verification: { type: 'string', description: 'Optional verification details.' },
|
|
1618
|
-
agentId: { type: 'string', description: 'Agent identifier. Defaults to MIO_CONTEXT agentId.' },
|
|
1619
|
-
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1620
|
-
traceId: { type: 'string', description: 'Optional custom trace ID. Auto-generated if omitted.' },
|
|
1621
|
-
},
|
|
1622
|
-
required: ['outcome'],
|
|
1623
|
-
},
|
|
1675
|
+
},
|
|
1624
1676
|
},
|
|
1625
1677
|
{
|
|
1626
|
-
name: 'mio.
|
|
1627
|
-
description: '
|
|
1678
|
+
name: 'mio.evolution.status',
|
|
1679
|
+
description: 'Report composed mio-agent-runtime module health from the canonical runtime module registry.',
|
|
1680
|
+
inputSchema: {
|
|
1681
|
+
type: 'object',
|
|
1682
|
+
properties: {},
|
|
1683
|
+
},
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
name: 'mio.host.capabilities',
|
|
1687
|
+
description: 'List installed desktop host adapters and the safe capability surface they expose to runtime modules.',
|
|
1688
|
+
inputSchema: {
|
|
1689
|
+
type: 'object',
|
|
1690
|
+
properties: {},
|
|
1691
|
+
},
|
|
1692
|
+
},
|
|
1693
|
+
{
|
|
1694
|
+
name: 'mio.evolution.shadow.record',
|
|
1695
|
+
description: 'Record a shadow comparison between legacy and modular runtime outputs without changing authority.',
|
|
1628
1696
|
inputSchema: {
|
|
1629
1697
|
type: 'object',
|
|
1630
|
-
required: ['goal'],
|
|
1631
1698
|
properties: {
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1699
|
+
label: { type: 'string', description: 'Human-readable comparison label.' },
|
|
1700
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1701
|
+
legacy: { type: 'object', description: 'Legacy path output snapshot.' },
|
|
1702
|
+
modular: { type: 'object', description: 'Modular path output snapshot.' },
|
|
1703
|
+
input: { type: 'object', description: 'Optional sanitized input snapshot.' },
|
|
1636
1704
|
},
|
|
1637
|
-
|
|
1705
|
+
required: ['legacy', 'modular'],
|
|
1638
1706
|
},
|
|
1639
1707
|
},
|
|
1640
1708
|
{
|
|
1641
|
-
name: 'mio.
|
|
1642
|
-
description: '
|
|
1709
|
+
name: 'mio.evolution.dual_write.record',
|
|
1710
|
+
description: 'Record a dual-write sample result for legacy and modular state paths without switching authority.',
|
|
1643
1711
|
inputSchema: {
|
|
1644
1712
|
type: 'object',
|
|
1645
1713
|
properties: {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1714
|
+
label: { type: 'string', description: 'Human-readable dual-write label.' },
|
|
1715
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1716
|
+
authoritative: { type: 'string', description: 'Authoritative path: legacy or modular. Defaults to legacy.' },
|
|
1717
|
+
legacyResult: { type: 'object', description: 'Legacy write result snapshot.' },
|
|
1718
|
+
modularResult: { type: 'object', description: 'Modular write result snapshot.' },
|
|
1719
|
+
record: { type: 'object', description: 'Optional sanitized record metadata.' },
|
|
1720
|
+
},
|
|
1721
|
+
required: ['legacyResult', 'modularResult'],
|
|
1651
1722
|
},
|
|
1652
1723
|
},
|
|
1653
1724
|
{
|
|
1654
|
-
name: 'mio.evolution.
|
|
1655
|
-
description: '
|
|
1725
|
+
name: 'mio.evolution.cutover.readiness',
|
|
1726
|
+
description: 'Assess whether shadow and dual-write samples are sufficient to consider modular runtime cutover.',
|
|
1656
1727
|
inputSchema: {
|
|
1657
1728
|
type: 'object',
|
|
1658
1729
|
properties: {
|
|
1659
|
-
project: { type: 'string', description: 'Project name
|
|
1660
|
-
|
|
1730
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1731
|
+
minShadowRuns: { type: 'number', description: 'Minimum shadow comparison samples required. Defaults to 5.' },
|
|
1732
|
+
maxMismatchRate: { type: 'number', description: 'Allowed shadow mismatch rate. Defaults to 0.' },
|
|
1733
|
+
},
|
|
1661
1734
|
},
|
|
1662
1735
|
},
|
|
1663
|
-
,
|
|
1664
1736
|
{
|
|
1665
|
-
name: 'mio.
|
|
1666
|
-
description: '
|
|
1737
|
+
name: 'mio.evolution.migration.plan',
|
|
1738
|
+
description: 'Preview a state migration plan between legacy and modular records without writing state.',
|
|
1667
1739
|
inputSchema: {
|
|
1668
1740
|
type: 'object',
|
|
1669
1741
|
properties: {
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
}
|
|
1742
|
+
legacyRecords: { type: 'array', items: { type: 'object' }, description: 'Legacy state records.' },
|
|
1743
|
+
modularRecords: { type: 'array', items: { type: 'object' }, description: 'Existing modular state records.' },
|
|
1744
|
+
},
|
|
1673
1745
|
},
|
|
1674
1746
|
},
|
|
1675
1747
|
{
|
|
1676
|
-
name: 'mio.
|
|
1677
|
-
description: '
|
|
1678
|
-
inputSchema: {
|
|
1748
|
+
name: 'mio.evolution.authority.plan',
|
|
1749
|
+
description: 'Create a gated authority switch plan from legacy to modular runtime without applying it.',
|
|
1750
|
+
inputSchema: {
|
|
1751
|
+
type: 'object',
|
|
1752
|
+
properties: {
|
|
1753
|
+
readiness: { type: 'object', description: 'Result from mio.evolution.cutover.readiness.' },
|
|
1754
|
+
from: { type: 'string', description: 'Current authoritative path. Defaults to legacy.' },
|
|
1755
|
+
to: { type: 'string', description: 'Target authoritative path. Defaults to modular.' },
|
|
1756
|
+
},
|
|
1757
|
+
required: ['readiness'],
|
|
1758
|
+
},
|
|
1679
1759
|
},
|
|
1680
1760
|
{
|
|
1681
|
-
name: 'mio.
|
|
1682
|
-
description: '
|
|
1683
|
-
inputSchema: {
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
const numIdeas = Math.min(Math.max(Number(args.numIdeas) || 3, 1), 10)
|
|
1690
|
-
const context = args.context ? String(args.context).trim() : ''
|
|
1691
|
-
const constraints = Array.isArray(args.constraints) ? args.constraints.map(String) : []
|
|
1692
|
-
|
|
1693
|
-
// Query Mio memory for related context
|
|
1694
|
-
const relatedRecords = readJsonl(memoryPath)
|
|
1695
|
-
.filter(r => r.archived !== true)
|
|
1696
|
-
.filter(r => {
|
|
1697
|
-
const text = (r.content || '').toLowerCase()
|
|
1698
|
-
const goalLower = goal.toLowerCase()
|
|
1699
|
-
// Support both space-separated (English) and continuous (Chinese) matching
|
|
1700
|
-
const goalWords = goalLower.split(/\s+/).filter(w => w.length > 2)
|
|
1701
|
-
// For Chinese: extract 2-char n-grams as search tokens
|
|
1702
|
-
const ngrams = []
|
|
1703
|
-
for (let i = 0; i < goalLower.length - 1; i++) {
|
|
1704
|
-
const ng = goalLower.slice(i, i + 2)
|
|
1705
|
-
if (ng.trim().length === 2) ngrams.push(ng)
|
|
1706
|
-
}
|
|
1707
|
-
const allTokens = [...new Set([...goalWords, ...ngrams])]
|
|
1708
|
-
return allTokens.some(t => text.includes(t))
|
|
1709
|
-
})
|
|
1710
|
-
.slice(0, 8)
|
|
1711
|
-
|
|
1712
|
-
const relatedMemoryText = relatedRecords.length > 0
|
|
1713
|
-
? relatedRecords.map(r => '- [' + r.kind + '] ' + (r.content || '').slice(0, 200)).join('\n')
|
|
1714
|
-
: '(no related memories found)'
|
|
1715
|
-
|
|
1716
|
-
const strategies = [
|
|
1717
|
-
{
|
|
1718
|
-
name: 'SCAMPER',
|
|
1719
|
-
prompt: 'Apply SCAMPER: Substitute, Combine, Adapt, Modify, Put to another use, Eliminate, Reverse. ' +
|
|
1720
|
-
'For goal: ' + goal + '. What novel combination or modification of existing elements could work?'
|
|
1721
|
-
},
|
|
1722
|
-
{
|
|
1723
|
-
name: 'Analogy',
|
|
1724
|
-
prompt: 'By analogy: What similar problem has been solved in a completely different domain? ' +
|
|
1725
|
-
'For goal: ' + goal + '. What approach from another field (biology, economics, gaming, logistics) transfers here?'
|
|
1726
|
-
},
|
|
1727
|
-
{
|
|
1728
|
-
name: 'First-Principles',
|
|
1729
|
-
prompt: 'First principles: Strip away all assumptions. What are the absolute basics of ' + goal + '? ' +
|
|
1730
|
-
'What would you build if you had no prior art, no constraints, no legacy?'
|
|
1731
|
-
},
|
|
1732
|
-
{
|
|
1733
|
-
name: 'Random-Stimulus',
|
|
1734
|
-
prompt: 'Random stimulus: Pick an unrelated concept (a relay race, a restaurant menu, a river delta, a beehive). ' +
|
|
1735
|
-
'How does it relate to ' + goal + '? What structural or procedural parallel exists?'
|
|
1736
|
-
},
|
|
1737
|
-
{
|
|
1738
|
-
name: 'Constraint-Inversion',
|
|
1739
|
-
prompt: 'Constraint inversion: Instead of ' + goal + ', what is the OPPOSITE goal? ' +
|
|
1740
|
-
'How would you deliberately make things worse? Now invert the inversion — what does that reveal about the real solution?'
|
|
1741
|
-
}
|
|
1742
|
-
]
|
|
1743
|
-
|
|
1744
|
-
const selectedStrategies = strategies.slice(0, numIdeas)
|
|
1745
|
-
const ideas = []
|
|
1746
|
-
|
|
1747
|
-
for (let i = 0; i < selectedStrategies.length; i++) {
|
|
1748
|
-
const s = selectedStrategies[i]
|
|
1749
|
-
const idea = {
|
|
1750
|
-
id: createId('idea'),
|
|
1751
|
-
title: 'Idea #' + (i + 1) + ' (' + s.name + '): ' + goal,
|
|
1752
|
-
description: 'Goal: ' + goal + '\n\n' +
|
|
1753
|
-
'Strategy: ' + s.name + '\n\n' +
|
|
1754
|
-
'Related Mio memory:\n' + relatedMemoryText + '\n\n' +
|
|
1755
|
-
(context ? 'Context: ' + context + '\n\n' : '') +
|
|
1756
|
-
(constraints.length > 0 ? 'Constraints:\n' + constraints.map(c => '- ' + c).join('\n') + '\n\n' : '') +
|
|
1757
|
-
'Strategy prompt: ' + s.prompt + '\n\n' +
|
|
1758
|
-
'Suggestion: Consider prototyping the idea and measuring its impact against prior decisions.',
|
|
1759
|
-
provenance: {
|
|
1760
|
-
strategy: s.name,
|
|
1761
|
-
relatedMemoryIds: relatedRecords.map(r => r.id).slice(0, 5),
|
|
1761
|
+
name: 'mio.evolution.cutover.apply',
|
|
1762
|
+
description: 'Dry-run an authority switch plan. This stage refuses real cutover and never mutates authority state.',
|
|
1763
|
+
inputSchema: {
|
|
1764
|
+
type: 'object',
|
|
1765
|
+
properties: {
|
|
1766
|
+
dryRun: { type: 'boolean', description: 'Must be true. Real cutover is intentionally gated.' },
|
|
1767
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1768
|
+
plan: { type: 'object', description: 'Authority switch plan from mio.evolution.authority.plan.' },
|
|
1762
1769
|
},
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1770
|
+
required: ['dryRun', 'plan'],
|
|
1771
|
+
},
|
|
1772
|
+
},
|
|
1773
|
+
{
|
|
1774
|
+
name: 'mio.task.record_outcome',
|
|
1775
|
+
description: 'Record task outcome with agent registry update and auto-claim experience reuse in one atomic call. Combines observer.ingest + agent stats + auto-claim for a complete task lifecycle closure.',
|
|
1776
|
+
inputSchema: {
|
|
1777
|
+
type: 'object',
|
|
1778
|
+
properties: {
|
|
1779
|
+
outcome: { type: 'string', description: 'Task outcome: success, failure, or aborted.' },
|
|
1780
|
+
task: { type: 'string', description: 'Brief task description.' },
|
|
1781
|
+
summary: { type: 'string', description: 'Optional task summary.' },
|
|
1782
|
+
verification: { type: 'string', description: 'Optional verification details.' },
|
|
1783
|
+
agentId: { type: 'string', description: 'Agent identifier. Defaults to MIO_CONTEXT agentId.' },
|
|
1784
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1785
|
+
traceId: { type: 'string', description: 'Optional custom trace ID. Auto-generated if omitted.' },
|
|
1786
|
+
},
|
|
1787
|
+
required: ['outcome'],
|
|
1788
|
+
},
|
|
1789
|
+
},
|
|
1790
|
+
{
|
|
1791
|
+
name: 'mio.creativity.status',
|
|
1792
|
+
description: 'Show creativity engine status: hypothesis counts, active/validated/rejected, recent top ideas.',
|
|
1793
|
+
inputSchema: { type: 'object', properties: {} },
|
|
1794
|
+
},
|
|
1795
|
+
{
|
|
1796
|
+
name: 'mio.creativity.list',
|
|
1797
|
+
description: 'List creativity hypotheses with optional status filter.',
|
|
1798
|
+
inputSchema: {
|
|
1799
|
+
type: 'object',
|
|
1800
|
+
properties: {
|
|
1801
|
+
status: { type: 'string', description: 'Filter by status: active, validated, rejected, draft.' },
|
|
1802
|
+
limit: { type: 'number', description: 'Max results. Default 20.' },
|
|
1803
|
+
},
|
|
1804
|
+
},
|
|
1805
|
+
},
|
|
1806
|
+
{
|
|
1807
|
+
name: 'mio.creativity.generate',
|
|
1808
|
+
description: 'Generate new creative hypotheses by combining concept sources via LLM. Returns ideas with novelty/feasibility/impact scores.',
|
|
1809
|
+
inputSchema: {
|
|
1810
|
+
type: 'object',
|
|
1811
|
+
properties: {
|
|
1812
|
+
sources: {
|
|
1813
|
+
type: 'array',
|
|
1814
|
+
description: 'Concept sources to combine. Each has name, content, type (knowledge/behavior/insight/failure), weight.',
|
|
1815
|
+
items: {
|
|
1816
|
+
type: 'object',
|
|
1817
|
+
properties: {
|
|
1818
|
+
name: { type: 'string' },
|
|
1819
|
+
content: { type: 'string' },
|
|
1820
|
+
type: { type: 'string' },
|
|
1821
|
+
weight: { type: 'number' },
|
|
1822
|
+
},
|
|
1823
|
+
required: ['name', 'content'],
|
|
1824
|
+
},
|
|
1825
|
+
},
|
|
1826
|
+
strategy: { type: 'string', description: 'Generation strategy: explore (cross-domain), signal (provocative), stable (conservative). Auto-selected if omitted.' },
|
|
1827
|
+
},
|
|
1828
|
+
required: ['sources'],
|
|
1829
|
+
},
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
name: 'mio.creativity.ferment',
|
|
1833
|
+
description: 'Ferment (review and refine) active hypotheses. Updates scores, can promote/reject ideas.',
|
|
1834
|
+
inputSchema: {
|
|
1835
|
+
type: 'object',
|
|
1836
|
+
properties: {
|
|
1837
|
+
limit: { type: 'number', description: 'Max hypotheses to ferment. Default 5.' },
|
|
1838
|
+
},
|
|
1839
|
+
},
|
|
1840
|
+
},
|
|
1841
|
+
|
|
1842
|
+
// ═══ Observer Research Pipeline ═══
|
|
1843
|
+
|
|
1844
|
+
// ═══ Insight Self-Observation ═══
|
|
1845
|
+
|
|
1846
|
+
...(insightStore ? [{
|
|
1847
|
+
name: 'mio.insight.status',
|
|
1848
|
+
description: 'Get insight engine status: total insights, unreported count, cooldown.',
|
|
1849
|
+
inputSchema: { type: 'object', properties: {} },
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
name: 'mio.insight.list',
|
|
1853
|
+
description: 'List stored insights with optional filters.',
|
|
1854
|
+
inputSchema: {
|
|
1855
|
+
type: 'object',
|
|
1856
|
+
properties: {
|
|
1857
|
+
unreported: { type: 'boolean', description: 'Only unreported insights' },
|
|
1858
|
+
minScore: { type: 'number', description: 'Minimum score filter' },
|
|
1859
|
+
detector: { type: 'string', description: 'Filter by detector name' },
|
|
1860
|
+
limit: { type: 'number', description: 'Max results (default 20)' },
|
|
1861
|
+
},
|
|
1862
|
+
},
|
|
1863
|
+
},
|
|
1864
|
+
{
|
|
1865
|
+
name: 'mio.insight.mark_reported',
|
|
1866
|
+
description: 'Mark insights as reported (acknowledged).',
|
|
1867
|
+
inputSchema: {
|
|
1868
|
+
type: 'object',
|
|
1869
|
+
properties: {
|
|
1870
|
+
ids: { type: 'array', items: { type: 'string' }, description: 'Insight IDs to mark' },
|
|
1871
|
+
},
|
|
1872
|
+
required: ['ids'],
|
|
1873
|
+
},
|
|
1874
|
+
},
|
|
1875
|
+
{
|
|
1876
|
+
name: 'mio.insight.generate',
|
|
1877
|
+
description: 'Run LLM-based insight generation from provided context data.',
|
|
1878
|
+
inputSchema: {
|
|
1879
|
+
type: 'object',
|
|
1880
|
+
properties: {
|
|
1881
|
+
memories: { type: 'array', items: { type: 'object' }, description: 'Memory entries to analyze' },
|
|
1882
|
+
summaries: { type: 'array', items: { type: 'string' }, description: 'Recent summaries' },
|
|
1883
|
+
plans: { type: 'array', items: { type: 'object' }, description: 'Active plans' },
|
|
1884
|
+
},
|
|
1885
|
+
},
|
|
1886
|
+
}] : []),
|
|
1887
|
+
|
|
1888
|
+
// ═══ Observer Research Pipeline ═══
|
|
1889
|
+
|
|
1890
|
+
...(ObserverStore ? [{
|
|
1891
|
+
name: 'mio.observer.world_model',
|
|
1892
|
+
description: 'Query the Observer world model: entities, events, trends, narratives, uncertainties, and relations.',
|
|
1893
|
+
inputSchema: {
|
|
1894
|
+
type: 'object',
|
|
1895
|
+
properties: {
|
|
1896
|
+
baseDir: { type: 'string', description: 'Observer data directory (default: .local/observer)' },
|
|
1897
|
+
},
|
|
1898
|
+
},
|
|
1899
|
+
},
|
|
1900
|
+
{
|
|
1901
|
+
name: 'mio.observer.trends',
|
|
1902
|
+
description: 'Get recent trend reports from the Observer pipeline.',
|
|
1903
|
+
inputSchema: {
|
|
1904
|
+
type: 'object',
|
|
1905
|
+
properties: {
|
|
1906
|
+
date: { type: 'string', description: 'Specific date (YYYY-MM-DD) or null for latest' },
|
|
1907
|
+
limit: { type: 'number', description: 'Max reports to return (default 7)' },
|
|
1908
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1909
|
+
},
|
|
1910
|
+
},
|
|
1911
|
+
},
|
|
1912
|
+
{
|
|
1913
|
+
name: 'mio.observer.research',
|
|
1914
|
+
description: 'List recent deep research results from the Observer pipeline.',
|
|
1915
|
+
inputSchema: {
|
|
1916
|
+
type: 'object',
|
|
1917
|
+
properties: {
|
|
1918
|
+
limit: { type: 'number', description: 'Max results (default 10)' },
|
|
1919
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1920
|
+
},
|
|
1921
|
+
},
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
name: 'mio.observer.insights',
|
|
1925
|
+
description: 'List Observer-generated insights (5-section structured articles).',
|
|
1926
|
+
inputSchema: {
|
|
1927
|
+
type: 'object',
|
|
1928
|
+
properties: {
|
|
1929
|
+
limit: { type: 'number', description: 'Max insights (default 20)' },
|
|
1930
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1931
|
+
},
|
|
1932
|
+
},
|
|
1933
|
+
},
|
|
1934
|
+
{
|
|
1935
|
+
name: 'mio.observer.status',
|
|
1936
|
+
description: 'Get Observer pipeline status: counts of observations, trends, topics, research, insights, essays, and world model entities.',
|
|
1937
|
+
inputSchema: {
|
|
1938
|
+
type: 'object',
|
|
1939
|
+
properties: {
|
|
1940
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1941
|
+
},
|
|
1942
|
+
},
|
|
1943
|
+
},
|
|
1944
|
+
{
|
|
1945
|
+
name: 'mio.observer.collect',
|
|
1946
|
+
description: 'Force data collection from all configured sources (RSS, Bilibili, HackerNews, GitHub, Douyin). Returns collected observations.',
|
|
1947
|
+
inputSchema: {
|
|
1948
|
+
type: 'object',
|
|
1949
|
+
properties: {
|
|
1950
|
+
sources: { type: 'array', items: { type: 'string' }, description: 'Specific sources to collect (default: all)' },
|
|
1951
|
+
keywords: { type: 'array', items: { type: 'string' }, description: 'Keyword filter (OR match)' },
|
|
1952
|
+
limit: { type: 'number', description: 'Max items per source (default 20)' },
|
|
1953
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1954
|
+
},
|
|
1955
|
+
},
|
|
1956
|
+
},
|
|
1957
|
+
{
|
|
1958
|
+
name: 'mio.observer.ferment',
|
|
1959
|
+
description: 'Run fermentation engine: find recurring themes across recent observations and optionally write an essay.',
|
|
1960
|
+
inputSchema: {
|
|
1961
|
+
type: 'object',
|
|
1962
|
+
properties: {
|
|
1963
|
+
session: { type: 'string', description: 'Session label: morning/afternoon/night (default: afternoon)' },
|
|
1964
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1965
|
+
},
|
|
1966
|
+
},
|
|
1967
|
+
},
|
|
1968
|
+
{
|
|
1969
|
+
name: 'mio.observer.essays',
|
|
1970
|
+
description: 'List published or draft essays from the Observer.',
|
|
1971
|
+
inputSchema: {
|
|
1972
|
+
type: 'object',
|
|
1973
|
+
properties: {
|
|
1974
|
+
type: { type: 'string', description: 'published or draft (default: published)' },
|
|
1975
|
+
limit: { type: 'number', description: 'Max essays (default 10)' },
|
|
1976
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1977
|
+
},
|
|
1978
|
+
},
|
|
1979
|
+
},
|
|
1980
|
+
{
|
|
1981
|
+
name: 'mio.observer.dag',
|
|
1982
|
+
description: 'Get DAG state machine status: today\'s task, recent summaries, retryable tasks.',
|
|
1983
|
+
inputSchema: {
|
|
1984
|
+
type: 'object',
|
|
1985
|
+
properties: {
|
|
1986
|
+
days: { type: 'number', description: 'Days to look back (default 7)' },
|
|
1987
|
+
baseDir: { type: 'string', description: 'Observer data directory' },
|
|
1988
|
+
},
|
|
1989
|
+
},
|
|
1990
|
+
}] : []),
|
|
1991
|
+
]
|
|
1992
|
+
|
|
1993
|
+
// ═══ Insight Self-Observation Handlers ═══
|
|
1994
|
+
|
|
1995
|
+
function insightStatus() {
|
|
1996
|
+
if (!insightStore) throw new Error('@akemi-mio/insight not installed')
|
|
1997
|
+
const all = insightStore.getAll()
|
|
1998
|
+
const unreported = insightStore.getUnreported()
|
|
1999
|
+
return {
|
|
2000
|
+
total: all.length,
|
|
2001
|
+
unreported: unreported.length,
|
|
2002
|
+
reported: all.length - unreported.length,
|
|
2003
|
+
highValue: insightStore.getHighValueUnreported(50, 0.7).length,
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
function insightList(args = {}) {
|
|
2008
|
+
if (!insightStore) throw new Error('@akemi-mio/insight not installed')
|
|
2009
|
+
let insights = insightStore.getAll()
|
|
2010
|
+
if (args.unreported) insights = insightStore.getUnreported()
|
|
2011
|
+
if (args.minScore) insights = insights.filter(i => i.score >= args.minScore)
|
|
2012
|
+
if (args.detector) insights = insights.filter(i => i.detector === args.detector)
|
|
2013
|
+
if (args.limit) insights = insights.slice(-args.limit)
|
|
2014
|
+
return insights
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
function insightMarkReported(args = {}) {
|
|
2018
|
+
if (!insightStore) throw new Error('@akemi-mio/insight not installed')
|
|
2019
|
+
if (!args.ids || !Array.isArray(args.ids)) throw new Error('ids array required')
|
|
2020
|
+
for (const id of args.ids) insightStore.markReported(id)
|
|
2021
|
+
return { marked: args.ids.length }
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
async function insightGenerate(args = {}) {
|
|
2025
|
+
if (!InsightGenerator) throw new Error('@akemi-mio/insight not installed')
|
|
2026
|
+
const generator = new InsightGenerator({ chatJson })
|
|
2027
|
+
const ctx = {
|
|
2028
|
+
memoryEntries: (args.memories || []).map(m => ({
|
|
2029
|
+
type: m.kind || m.type || 'note',
|
|
2030
|
+
content: m.content || '',
|
|
2031
|
+
createdAt: m.createdAt || m.timestamp || Date.now(),
|
|
2032
|
+
})),
|
|
2033
|
+
summaries: args.summaries || [],
|
|
2034
|
+
interactionCount: 0,
|
|
2035
|
+
plans: (args.plans || []).map(p => ({
|
|
2036
|
+
title: p.title || p.name || '',
|
|
2037
|
+
status: p.status || 'unknown',
|
|
2038
|
+
updatedAt: p.updatedAt || Date.now(),
|
|
2039
|
+
steps: p.steps || [],
|
|
2040
|
+
createdAt: p.createdAt || Date.now(),
|
|
2041
|
+
})),
|
|
2042
|
+
eventCount: 0,
|
|
2043
|
+
}
|
|
2044
|
+
const insights = await generator.generate(ctx)
|
|
2045
|
+
if (insightStore) {
|
|
2046
|
+
for (const i of insights) insightStore.addMany([i])
|
|
2047
|
+
}
|
|
2048
|
+
return { generated: insights.length, insights }
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
// ═══ Observer Research Pipeline Handlers ═══
|
|
2052
|
+
|
|
2053
|
+
function getObserverStore(baseDir) {
|
|
2054
|
+
if (!ObserverStore) throw new Error('@akemi-mio/observer not installed')
|
|
2055
|
+
return new ObserverStore(baseDir || path.join(process.cwd(), '.local', 'observer'))
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2058
|
+
function getObserverBaseDir(args) {
|
|
2059
|
+
return args.baseDir || path.join(process.cwd(), '.local', 'observer')
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
function readJsonSafe(filePath) {
|
|
2063
|
+
if (!fs.existsSync(filePath)) return null
|
|
2064
|
+
try { return JSON.parse(fs.readFileSync(filePath, 'utf8')) } catch { return null }
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
function readJsonlSafe(filePath) {
|
|
2068
|
+
if (!fs.existsSync(filePath)) return []
|
|
2069
|
+
try {
|
|
2070
|
+
return fs.readFileSync(filePath, 'utf8').trim().split('\n').filter(Boolean)
|
|
2071
|
+
.map(l => { try { return JSON.parse(l) } catch { return null } }).filter(Boolean)
|
|
2072
|
+
} catch { return [] }
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
function observerWorldModel(args = {}) {
|
|
2076
|
+
const baseDir = getObserverBaseDir(args)
|
|
2077
|
+
const wmDir = path.join(baseDir, 'world_model')
|
|
2078
|
+
return {
|
|
2079
|
+
entities: readJsonSafe(path.join(wmDir, 'entities.json')) || [],
|
|
2080
|
+
events: readJsonSafe(path.join(wmDir, 'events.json')) || [],
|
|
2081
|
+
trends: readJsonSafe(path.join(wmDir, 'trends.json')) || [],
|
|
2082
|
+
narratives: readJsonSafe(path.join(wmDir, 'narratives.json')) || [],
|
|
2083
|
+
}
|
|
2084
|
+
}
|
|
2085
|
+
|
|
2086
|
+
function observerTrends(args = {}) {
|
|
2087
|
+
const baseDir = getObserverBaseDir(args)
|
|
2088
|
+
const trendsDir = path.join(baseDir, 'trends')
|
|
2089
|
+
if (!fs.existsSync(trendsDir)) return []
|
|
2090
|
+
if (args.date) {
|
|
2091
|
+
const report = readJsonSafe(path.join(trendsDir, `${args.date}.json`))
|
|
2092
|
+
return report ? [report] : []
|
|
2093
|
+
}
|
|
2094
|
+
const files = fs.readdirSync(trendsDir).filter(f => f.endsWith('.json')).sort().reverse()
|
|
2095
|
+
return files.slice(0, args.limit || 7).map(f => readJsonSafe(path.join(trendsDir, f))).filter(Boolean)
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
function observerResearch(args = {}) {
|
|
2099
|
+
const baseDir = getObserverBaseDir(args)
|
|
2100
|
+
const researchDir = path.join(baseDir, 'research')
|
|
2101
|
+
if (!fs.existsSync(researchDir)) return []
|
|
2102
|
+
const files = fs.readdirSync(researchDir).filter(f => f.endsWith('.json')).sort().reverse()
|
|
2103
|
+
return files.slice(0, args.limit || 10).map(f => readJsonSafe(path.join(researchDir, f))).filter(Boolean)
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
function observerInsights(args = {}) {
|
|
2107
|
+
const baseDir = getObserverBaseDir(args)
|
|
2108
|
+
const insightsDir = path.join(baseDir, 'insights')
|
|
2109
|
+
if (!fs.existsSync(insightsDir)) return []
|
|
2110
|
+
const files = fs.readdirSync(insightsDir).filter(f => f.endsWith('.json')).sort().reverse()
|
|
2111
|
+
return files.slice(0, args.limit || 20).map(f => readJsonSafe(path.join(insightsDir, f))).filter(Boolean)
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
function observerStatus(args = {}) {
|
|
2115
|
+
const baseDir = getObserverBaseDir(args)
|
|
2116
|
+
const dirs = ['observations', 'trends', 'topics', 'research', 'insights', 'world_model', 'essays']
|
|
2117
|
+
const status = {}
|
|
2118
|
+
for (const d of dirs) {
|
|
2119
|
+
const dir = path.join(baseDir, d)
|
|
2120
|
+
if (fs.existsSync(dir)) {
|
|
2121
|
+
status[d] = fs.readdirSync(dir).filter(f => !f.startsWith('.')).length
|
|
2122
|
+
} else {
|
|
2123
|
+
status[d] = 0
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
return status
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
function observerCollect(args = {}) {
|
|
2130
|
+
if (!ObserverService) throw new Error('@akemi-mio/observer not installed')
|
|
2131
|
+
const baseDir = getObserverBaseDir(args)
|
|
2132
|
+
const service = new ObserverService({ baseDir, collectorConfig: {} })
|
|
2133
|
+
const sources = args.sources || ['bilibili', 'hackernews', 'github', 'douyin', 'rss']
|
|
2134
|
+
const allObs = []
|
|
2135
|
+
for (const src of sources) {
|
|
2136
|
+
try {
|
|
2137
|
+
const collectFn = service.collectBySource ? service.collectBySource : service.collect
|
|
2138
|
+
if (collectFn) {
|
|
2139
|
+
const result = collectFn.call(service, [src], args.keywords || [], args.limit || 20)
|
|
2140
|
+
if (Array.isArray(result)) allObs.push(...result)
|
|
2141
|
+
else if (result && typeof result === 'object') {
|
|
2142
|
+
for (const [key, items] of Object.entries(result)) allObs.push(...items)
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
} catch (e) {
|
|
2146
|
+
allObs.push({ source: src, error: e.message })
|
|
2147
|
+
}
|
|
2148
|
+
}
|
|
2149
|
+
return { collected: allObs.length, observations: allObs }
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
function observerFerment(args = {}) {
|
|
2153
|
+
if (!ObserverService) throw new Error('@akemi-mio/observer not installed')
|
|
2154
|
+
const baseDir = getObserverBaseDir(args)
|
|
2155
|
+
const service = new ObserverService({ baseDir, collectorConfig: {} })
|
|
2156
|
+
const session = args.session || 'afternoon'
|
|
2157
|
+
try {
|
|
2158
|
+
const fermentation = service.getFermentation()
|
|
2159
|
+
if (fermentation && fermentation.ferment) return fermentation.ferment(session)
|
|
2160
|
+
return { status: 'fermentation engine available but no ferment method' }
|
|
2161
|
+
} catch (e) {
|
|
2162
|
+
return { error: e.message }
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
|
|
2166
|
+
function observerEssays(args = {}) {
|
|
2167
|
+
const baseDir = getObserverBaseDir(args)
|
|
2168
|
+
const type = args.type || 'published'
|
|
2169
|
+
const essaysDir = path.join(baseDir, 'essays', type)
|
|
2170
|
+
if (!fs.existsSync(essaysDir)) return []
|
|
2171
|
+
const files = fs.readdirSync(essaysDir).filter(f => f.endsWith('.md')).sort().reverse()
|
|
2172
|
+
return files.slice(0, args.limit || 10).map(f => {
|
|
2173
|
+
const content = fs.readFileSync(path.join(essaysDir, f), 'utf8')
|
|
2174
|
+
return { file: f, type, content, created: fs.statSync(path.join(essaysDir, f)).birthtime.toISOString() }
|
|
2175
|
+
})
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
function observerDag(args = {}) {
|
|
2179
|
+
const baseDir = getObserverBaseDir(args)
|
|
2180
|
+
const days = args.days || 7
|
|
2181
|
+
const summaries = []
|
|
2182
|
+
const todayDate = new Date()
|
|
2183
|
+
for (let i = 0; i < days; i++) {
|
|
2184
|
+
const d = new Date(todayDate)
|
|
2185
|
+
d.setDate(d.getDate() - i)
|
|
2186
|
+
const dateStr = d.toISOString().slice(0, 10)
|
|
2187
|
+
const filePath = path.join(baseDir, 'summaries', `${dateStr}.jsonl`)
|
|
2188
|
+
const items = readJsonlSafe(filePath)
|
|
2189
|
+
if (items.length) summaries.push({ date: dateStr, summaries: items })
|
|
2190
|
+
}
|
|
2191
|
+
return { summaries, summaryCount: summaries.reduce((a, s) => a + s.summaries.length, 0) }
|
|
2192
|
+
}
|
|
2193
|
+
|
|
1800
2194
|
async function callTool(name, args = {}) {
|
|
1801
2195
|
switch (name) {
|
|
1802
2196
|
case 'mio.memory.query':
|
|
@@ -1833,77 +2227,65 @@ async function callTool(name, args = {}) {
|
|
|
1833
2227
|
return listAgents(args)
|
|
1834
2228
|
case 'mio.agent.report':
|
|
1835
2229
|
return reportAgent(args)
|
|
1836
|
-
case 'mio.evolution.report':
|
|
1837
|
-
return evolutionReport(args)
|
|
1838
|
-
case 'mio.
|
|
1839
|
-
return
|
|
1840
|
-
case 'mio.
|
|
1841
|
-
return
|
|
1842
|
-
case 'mio.
|
|
1843
|
-
return
|
|
1844
|
-
case 'mio.evolution.
|
|
1845
|
-
return
|
|
1846
|
-
case 'mio.
|
|
1847
|
-
return
|
|
1848
|
-
case 'mio.
|
|
1849
|
-
return
|
|
1850
|
-
case 'mio.
|
|
1851
|
-
return
|
|
1852
|
-
|
|
2230
|
+
case 'mio.evolution.report':
|
|
2231
|
+
return evolutionReport(args)
|
|
2232
|
+
case 'mio.evolution.status':
|
|
2233
|
+
return getEvolutionStatus()
|
|
2234
|
+
case 'mio.host.capabilities':
|
|
2235
|
+
return listHostCapabilities()
|
|
2236
|
+
case 'mio.evolution.shadow.record':
|
|
2237
|
+
return evolutionCutover.recordShadowComparison(args)
|
|
2238
|
+
case 'mio.evolution.dual_write.record':
|
|
2239
|
+
return evolutionCutover.recordDualWrite(args)
|
|
2240
|
+
case 'mio.evolution.cutover.readiness':
|
|
2241
|
+
return evolutionCutover.cutoverReadiness(args)
|
|
2242
|
+
case 'mio.evolution.migration.plan':
|
|
2243
|
+
return evolutionCutover.migrationPlan(args)
|
|
2244
|
+
case 'mio.evolution.authority.plan':
|
|
2245
|
+
return evolutionCutover.authorityPlan(args)
|
|
2246
|
+
case 'mio.evolution.cutover.apply':
|
|
2247
|
+
return evolutionCutover.applyCutover(args)
|
|
2248
|
+
case 'mio.task.record_outcome':
|
|
2249
|
+
return recordTaskOutcome(args)
|
|
2250
|
+
case 'mio.creativity.status':
|
|
2251
|
+
return creativityEngine.status()
|
|
2252
|
+
case 'mio.creativity.list':
|
|
2253
|
+
return creativityEngine.list(args)
|
|
2254
|
+
case 'mio.creativity.generate':
|
|
2255
|
+
return creativityEngine.generate(args.sources, args.strategy)
|
|
2256
|
+
case 'mio.creativity.ferment':
|
|
2257
|
+
return creativityEngine.ferment(args.limit)
|
|
2258
|
+
case 'mio.insight.status':
|
|
2259
|
+
return insightStatus()
|
|
2260
|
+
case 'mio.insight.list':
|
|
2261
|
+
return insightList(args)
|
|
2262
|
+
case 'mio.insight.mark_reported':
|
|
2263
|
+
return insightMarkReported(args)
|
|
2264
|
+
case 'mio.insight.generate':
|
|
2265
|
+
return insightGenerate(args)
|
|
2266
|
+
case 'mio.observer.world_model':
|
|
2267
|
+
return observerWorldModel(args)
|
|
2268
|
+
case 'mio.observer.trends':
|
|
2269
|
+
return observerTrends(args)
|
|
2270
|
+
case 'mio.observer.research':
|
|
2271
|
+
return observerResearch(args)
|
|
2272
|
+
case 'mio.observer.insights':
|
|
2273
|
+
return observerInsights(args)
|
|
2274
|
+
case 'mio.observer.status':
|
|
2275
|
+
return observerStatus(args)
|
|
2276
|
+
case 'mio.observer.collect':
|
|
2277
|
+
return observerCollect(args)
|
|
2278
|
+
case 'mio.observer.ferment':
|
|
2279
|
+
return observerFerment(args)
|
|
2280
|
+
case 'mio.observer.essays':
|
|
2281
|
+
return observerEssays(args)
|
|
2282
|
+
case 'mio.observer.dag':
|
|
2283
|
+
return observerDag(args)
|
|
2284
|
+
default:
|
|
1853
2285
|
throw new Error(`Unknown tool: ${name}`)
|
|
1854
2286
|
}
|
|
1855
2287
|
}
|
|
1856
2288
|
|
|
1857
|
-
const DASHBOARD_PORT = 8787
|
|
1858
|
-
const dashboardPidPath = () => path.join(dataDir, 'dashboard.pid')
|
|
1859
|
-
|
|
1860
|
-
function isDashboardRunning() {
|
|
1861
|
-
const pidFile = dashboardPidPath()
|
|
1862
|
-
if (!fs.existsSync(pidFile)) return false
|
|
1863
|
-
try {
|
|
1864
|
-
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10)
|
|
1865
|
-
process.kill(pid, 0)
|
|
1866
|
-
return true
|
|
1867
|
-
} catch (_) {
|
|
1868
|
-
return false
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
|
|
1872
|
-
function handleDashboardStart(args = {}) {
|
|
1873
|
-
const port = parseInt(args.port || process.env.MIO_DASHBOARD_PORT || DASHBOARD_PORT, 10)
|
|
1874
|
-
const url = 'http://localhost:' + port
|
|
1875
|
-
if (isDashboardRunning()) {
|
|
1876
|
-
if (args.open) { try { require('child_process').exec('start ' + url) } catch (_) {} }
|
|
1877
|
-
return { url: url, running: true, message: 'Dashboard already running at ' + url }
|
|
1878
|
-
}
|
|
1879
|
-
const serverScript = path.join(__dirname, 'dashboard-server.js')
|
|
1880
|
-
const child = require('child_process').spawn(process.execPath, [serverScript, String(port)], {
|
|
1881
|
-
env: Object.assign({}, process.env, { MIO_DATA_DIR: dataDir }),
|
|
1882
|
-
detached: true,
|
|
1883
|
-
stdio: 'ignore',
|
|
1884
|
-
})
|
|
1885
|
-
child.unref()
|
|
1886
|
-
fs.writeFileSync(dashboardPidPath(), String(child.pid))
|
|
1887
|
-
if (args.open) { try { require('child_process').exec('start ' + url) } catch (_) {} }
|
|
1888
|
-
return { url: url, running: true, pid: child.pid, message: 'Dashboard started at ' + url }
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
function handleDashboardStop() {
|
|
1892
|
-
const pidFile = dashboardPidPath()
|
|
1893
|
-
if (!fs.existsSync(pidFile)) return { running: false, message: 'Dashboard not running.' }
|
|
1894
|
-
try {
|
|
1895
|
-
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10)
|
|
1896
|
-
process.kill(pid, 'SIGTERM')
|
|
1897
|
-
} catch (_) {}
|
|
1898
|
-
try { fs.unlinkSync(pidFile) } catch (_) {}
|
|
1899
|
-
return { running: false, message: 'Dashboard stopped.' }
|
|
1900
|
-
}
|
|
1901
|
-
|
|
1902
|
-
function handleDashboardStatus() {
|
|
1903
|
-
const running = isDashboardRunning()
|
|
1904
|
-
return { running: running, url: running ? 'http://localhost:' + DASHBOARD_PORT : null }
|
|
1905
|
-
}
|
|
1906
|
-
|
|
1907
2289
|
function jsonRpcResult(id, result) {
|
|
1908
2290
|
return { jsonrpc: '2.0', id, result }
|
|
1909
2291
|
}
|
|
@@ -2218,65 +2600,4 @@ function recordTaskOutcome(args = {}) {
|
|
|
2218
2600
|
}
|
|
2219
2601
|
}
|
|
2220
2602
|
|
|
2221
|
-
|
|
2222
|
-
function evolutionSuggest(args = {}) {
|
|
2223
|
-
const project = args.project || projectName()
|
|
2224
|
-
const now = Date.now()
|
|
2225
|
-
const memories = readJsonl(memoryPath)
|
|
2226
|
-
const traces = readJsonl(tracePath)
|
|
2227
|
-
const reuses = readJsonl(experienceReusePath)
|
|
2228
|
-
const ideas = fs.existsSync(ideasPath)
|
|
2229
|
-
? fs.readFileSync(ideasPath, 'utf8').split('\n').filter(Boolean).map(l => { try { return JSON.parse(l) } catch (_) { return null } }).filter(Boolean)
|
|
2230
|
-
: []
|
|
2231
|
-
const agentIds = new Set()
|
|
2232
|
-
traces.forEach((t) => { if (t.agent) agentIds.add(t.agent) })
|
|
2233
|
-
const outcomes = traces.filter((t) => t.event_type === 'task_outcome')
|
|
2234
|
-
const successes = outcomes.filter((t) => t.outcome === 'success').length
|
|
2235
|
-
const successRate = outcomes.length > 0 ? Math.round((successes / outcomes.length) * 100) : 0
|
|
2236
|
-
const verifiedReuses = reuses.filter((r) => r.confirmed && r.reuse && r.behaviorChanged && r.outcomeImproved)
|
|
2237
|
-
const pendingReuses = reuses.filter((r) => r.source === 'auto_claim' && !r.confirmed)
|
|
2238
|
-
const decisions = memories.filter((m) => m.kind === 'decision')
|
|
2239
|
-
const problems = memories.filter((m) => m.kind === 'problem')
|
|
2240
|
-
const shortMemories = memories.filter((m) => String(m.content || '').trim().length < 20)
|
|
2241
|
-
const ideasWithoutMemory = ideas.filter((i) => !i.provenance?.relatedMemoryIds?.length)
|
|
2242
|
-
const suggestions = []
|
|
2243
|
-
|
|
2244
|
-
if (agentIds.size >= 2) {
|
|
2245
|
-
const agentStats = []
|
|
2246
|
-
for (const id of agentIds) {
|
|
2247
|
-
const ao = outcomes.filter((t) => t.agent === id)
|
|
2248
|
-
const as = ao.filter((t) => t.outcome === 'success').length
|
|
2249
|
-
agentStats.push({ id, total: ao.length, success: as, rate: ao.length > 0 ? Math.round(as / ao.length * 100) : 0 })
|
|
2250
|
-
}
|
|
2251
|
-
agentStats.sort((a, b) => b.rate - a.rate)
|
|
2252
|
-
const best = agentStats[0], worst = agentStats[agentStats.length - 1]
|
|
2253
|
-
if (best.total >= 5 && worst.total >= 5 && best.rate - worst.rate >= 15) {
|
|
2254
|
-
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' })
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
if (shortMemories.length > 5 && memories.length > 10) {
|
|
2258
|
-
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' })
|
|
2259
|
-
}
|
|
2260
|
-
if (pendingReuses.length >= 5) {
|
|
2261
|
-
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' })
|
|
2262
|
-
}
|
|
2263
|
-
const crossAgent = verifiedReuses.filter((r) => r.sourceAgent !== r.targetAgent)
|
|
2264
|
-
if (agentIds.size >= 2 && crossAgent.length === 0) {
|
|
2265
|
-
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' })
|
|
2266
|
-
}
|
|
2267
|
-
if (ideas.length > 0 && ideasWithoutMemory.length > ideas.length * 0.5) {
|
|
2268
|
-
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' })
|
|
2269
|
-
}
|
|
2270
|
-
if (problems.length >= 3) {
|
|
2271
|
-
const recent = problems.filter((p) => (now - new Date(p.timestamp).getTime()) < 7 * 86400000)
|
|
2272
|
-
if (recent.length >= 2) {
|
|
2273
|
-
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' })
|
|
2274
|
-
}
|
|
2275
|
-
}
|
|
2276
|
-
suggestions.sort((a, b) => ({ high: 0, medium: 1, low: 2 }[a.priority] || 2) - ({ high: 0, medium: 1, low: 2 }[b.priority] || 2))
|
|
2277
|
-
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 }
|
|
2278
|
-
try { fs.appendFileSync(suggestionsPath, JSON.stringify(record) + '\n') } catch (_) {}
|
|
2279
|
-
return record
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
2603
|
module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }
|