mio-agent-runtime 0.3.15 → 0.4.0
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
|
@@ -21,7 +21,12 @@ Mio Agent Enhancement Layer 的最小 MCP 实现。
|
|
|
21
21
|
- `mio.phase0.report` — 生成当前 Phase 0 验证报告,返回门槛状态、复用漏斗(含 `pendingAutoClaims`,即待确认的 auto_claim 数)、Agent 覆盖(含每个 host 的健康状态)、trace outcome 分布;支持 `format: markdown` 直接返回渲染报告。
|
|
22
22
|
- `mio.task.route` — 把任务路由到最相关的**已验证经验**与相关记忆(P1):对任务描述做混合检索匹配 verified 经验,返回可直接采用的模式(含 `reuseCount` 复用次数、确认状态、来源/目标 Agent 与记忆内容),把「查询响应」升级为「决策影响」;路由结果会进入查询日志,便于后续 `task_outcome` 自动关联复用证据。
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
- `mio.agent.register` — 注册或更新 Agent 到 Mio Agent Registry(按 agentId+project 去重):首次调用创建记录(registered=true),后续调用更新 lastSeenAt 并递增 sessionCount。支持 hostType(mcp/observer/plugin)和 capabilities。
|
|
25
|
+
- `mio.agent.list` — 列出所有已注册 Agent,支持按 project 过滤。返回每个 Agent 的 id、agentId、hostType、capabilities、注册/最近活跃时间、会话数。
|
|
26
|
+
|
|
27
|
+
- `mio.evolution.report` — 生成跨 Agent 演化报告(ADR-017 Evolution 平面):生态系统摘要(agent/task/memory/reuse 总量)、Agent 性能对比(成功率/memory 数/verified reuse)、跨 Agent 复用模式、Memory 健康度(质量评分/低质量记录/缺失字段)、优化建议(路由/证据确认/记忆质量/覆盖度,按优先级排序)。支持 `period`(24h/7d/30d/all)和 `project` 过滤。- `mio.agent.report` — 生成 Agent 性能报告:聚合 task_outcome(成功率)、memory 数量、experience reuse(总次数/verified 数量)和活跃状态。支持指定 agentId 或 project 过滤。
|
|
28
|
+
|
|
29
|
+
- `mio.task.record_outcome` — 一键记录任务结果:原子写入 traces.jsonl + 更新 Agent Registry(taskCount/successCount/failureCount)+ 自动关联 experience reuse auto-claim。替代手动调用 observer.ingest + agent.register 的分离流程,完成任务生命周期闭环。## 项目识别
|
|
25
30
|
|
|
26
31
|
不传 `project` 时,服务会通过当前 Git 仓库自动识别项目名:
|
|
27
32
|
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const path = require('path')
|
|
6
|
-
const os = require('os')
|
|
7
6
|
const crypto = require('crypto')
|
|
8
7
|
const readline = require('readline')
|
|
9
8
|
const { execFileSync } = require('child_process')
|
|
@@ -12,14 +11,13 @@ const SERVER_INFO = { name: 'mio-intelligence-mcp', version: '0.1.0' }
|
|
|
12
11
|
const PROTOCOL_VERSION = '2024-11-05'
|
|
13
12
|
|
|
14
13
|
const dataDir = path.resolve(
|
|
15
|
-
process.env.MIO_DATA_DIR ||
|
|
16
|
-
process.env.MIO_HOME ||
|
|
17
|
-
path.join(os.homedir(), '.mio-intelligence')
|
|
14
|
+
process.env.MIO_DATA_DIR || path.join(process.cwd(), '.mio-intelligence')
|
|
18
15
|
)
|
|
19
16
|
const memoryPath = path.join(dataDir, 'memory.jsonl')
|
|
20
17
|
const tracePath = path.join(dataDir, 'traces.jsonl')
|
|
21
18
|
const experienceReusePath = path.join(dataDir, 'experience_reuse.jsonl')
|
|
22
19
|
const queryPath = path.join(dataDir, 'queries.jsonl')
|
|
20
|
+
const agentsPath = path.join(dataDir, 'agents.jsonl')
|
|
23
21
|
const subscriptionPath = path.join(dataDir, 'subscriptions.jsonl')
|
|
24
22
|
const digestStatePath = path.join(dataDir, 'digest_state.json')
|
|
25
23
|
fs.mkdirSync(dataDir, { recursive: true })
|
|
@@ -1128,6 +1126,94 @@ function listExperienceReuse(args = {}) {
|
|
|
1128
1126
|
}
|
|
1129
1127
|
}
|
|
1130
1128
|
|
|
1129
|
+
function registerAgent(args = {}) {
|
|
1130
|
+
const agentId = String(args.agentId || '').trim()
|
|
1131
|
+
if (!agentId) throw new Error('agent.register requires a non-empty agentId')
|
|
1132
|
+
const project = args.project || projectName()
|
|
1133
|
+
const hostType = args.hostType || 'mcp'
|
|
1134
|
+
const capabilities = Array.isArray(args.capabilities) ? args.capabilities : []
|
|
1135
|
+
const now = new Date().toISOString()
|
|
1136
|
+
const agents = readJsonl(agentsPath)
|
|
1137
|
+
const existing = agents.find((a) => a.agentId === agentId && a.project === project)
|
|
1138
|
+
let result
|
|
1139
|
+
if (existing) {
|
|
1140
|
+
existing.lastSeenAt = now
|
|
1141
|
+
existing.sessionCount = (existing.sessionCount || 1) + 1
|
|
1142
|
+
existing.hostType = hostType
|
|
1143
|
+
if (capabilities.length > 0) existing.capabilities = capabilities
|
|
1144
|
+
result = existing
|
|
1145
|
+
} else {
|
|
1146
|
+
result = {
|
|
1147
|
+
id: createId('agent'),
|
|
1148
|
+
agentId,
|
|
1149
|
+
hostType,
|
|
1150
|
+
capabilities,
|
|
1151
|
+
registeredAt: now,
|
|
1152
|
+
lastSeenAt: now,
|
|
1153
|
+
sessionCount: 1,
|
|
1154
|
+
project,
|
|
1155
|
+
}
|
|
1156
|
+
agents.push(result)
|
|
1157
|
+
}
|
|
1158
|
+
writeJsonl(agentsPath, agents)
|
|
1159
|
+
return { id: result.id, agentId: result.agentId, registered: !existing, sessionCount: result.sessionCount, hostType: result.hostType, capabilities: result.capabilities }
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
function listAgents(args = {}) {
|
|
1163
|
+
const project = args.project || null
|
|
1164
|
+
let agents = readJsonl(agentsPath)
|
|
1165
|
+
if (project) agents = agents.filter((a) => a.project === project)
|
|
1166
|
+
return {
|
|
1167
|
+
count: agents.length,
|
|
1168
|
+
agents: agents.map((a) => ({
|
|
1169
|
+
id: a.id, agentId: a.agentId, hostType: a.hostType,
|
|
1170
|
+
capabilities: a.capabilities, registeredAt: a.registeredAt,
|
|
1171
|
+
lastSeenAt: a.lastSeenAt, sessionCount: a.sessionCount,
|
|
1172
|
+
taskCount: a.taskCount || 0, successCount: a.successCount || 0, failureCount: a.failureCount || 0,
|
|
1173
|
+
project: a.project,
|
|
1174
|
+
})),
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
function reportAgent(args = {}) {
|
|
1179
|
+
const targetAgent = args.agentId || null
|
|
1180
|
+
const project = args.project || projectName()
|
|
1181
|
+
const agents = readJsonl(agentsPath)
|
|
1182
|
+
const filtered = agents.filter((a) =>
|
|
1183
|
+
(!targetAgent || a.agentId === targetAgent) && (!project || a.project === project)
|
|
1184
|
+
)
|
|
1185
|
+
const traces = readJsonl(tracePath)
|
|
1186
|
+
const memories = readJsonl(memoryPath)
|
|
1187
|
+
const reuses = readJsonl(experienceReusePath)
|
|
1188
|
+
const reports = filtered.map((agent) => {
|
|
1189
|
+
const agentTraces = traces.filter((t) => t.agent === agent.agentId)
|
|
1190
|
+
const outcomes = agentTraces.filter((t) => t.event_type === 'task_outcome')
|
|
1191
|
+
const successes = outcomes.filter((t) => t.outcome === 'success').length
|
|
1192
|
+
const failures = outcomes.filter((t) => t.outcome === 'failure').length
|
|
1193
|
+
const agentMemories = memories.filter((m) => m.source === agent.agentId)
|
|
1194
|
+
const agentReuses = reuses.filter((r) => r.sourceAgent === agent.agentId || r.targetAgent === agent.agentId)
|
|
1195
|
+
const verified = agentReuses.filter((r) => r.confirmed && r.reuse && r.behaviorChanged && r.outcomeImproved)
|
|
1196
|
+
const regTasks = agent.taskCount || 0
|
|
1197
|
+
const regSuccess = agent.successCount || 0
|
|
1198
|
+
const regFail = agent.failureCount || 0
|
|
1199
|
+
const taskTotal = regTasks > 0 ? regTasks : outcomes.length
|
|
1200
|
+
const taskSuccess = regTasks > 0 ? regSuccess : successes
|
|
1201
|
+
const taskFail = regTasks > 0 ? regFail : failures
|
|
1202
|
+
return {
|
|
1203
|
+
agentId: agent.agentId,
|
|
1204
|
+
hostType: agent.hostType,
|
|
1205
|
+
sessionCount: agent.sessionCount,
|
|
1206
|
+
registeredAt: agent.registeredAt,
|
|
1207
|
+
lastSeenAt: agent.lastSeenAt,
|
|
1208
|
+
taskOutcomes: { total: taskTotal, success: taskSuccess, failure: taskFail, successRate: taskTotal > 0 ? Math.round((taskSuccess / taskTotal) * 100) : 0 },
|
|
1209
|
+
memories: agentMemories.length,
|
|
1210
|
+
experienceReuses: { total: agentReuses.length, verified: verified.length },
|
|
1211
|
+
active: (Date.now() - new Date(agent.lastSeenAt).getTime()) < 24 * 60 * 60 * 1000,
|
|
1212
|
+
}
|
|
1213
|
+
})
|
|
1214
|
+
return { project, count: reports.length, reports }
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1131
1217
|
function routeTask(args = {}) {
|
|
1132
1218
|
const task = String(args.task || '').trim()
|
|
1133
1219
|
if (!task) throw new Error('task.route requires a non-empty task')
|
|
@@ -1471,6 +1557,69 @@ const TOOLS = [
|
|
|
1471
1557
|
required: ['task'],
|
|
1472
1558
|
},
|
|
1473
1559
|
},
|
|
1560
|
+
{
|
|
1561
|
+
name: 'mio.agent.register',
|
|
1562
|
+
description: 'Register or update an agent in the Mio Agent Registry. Upserts by agentId+project; updates lastSeenAt and sessionCount.',
|
|
1563
|
+
inputSchema: {
|
|
1564
|
+
type: 'object',
|
|
1565
|
+
properties: {
|
|
1566
|
+
agentId: { type: 'string', description: 'Unique agent identifier (e.g. codex, opencode).' },
|
|
1567
|
+
hostType: { type: 'string', description: 'Host type: mcp, observer, or plugin. Defaults to mcp.' },
|
|
1568
|
+
capabilities: { type: 'array', items: { type: 'string' }, description: 'Agent capabilities.' },
|
|
1569
|
+
project: { type: 'string', description: 'Project name. Defaults to current directory name.' },
|
|
1570
|
+
},
|
|
1571
|
+
required: ['agentId'],
|
|
1572
|
+
},
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
name: 'mio.agent.list',
|
|
1576
|
+
description: 'List all registered agents, optionally filtered by project.',
|
|
1577
|
+
inputSchema: {
|
|
1578
|
+
type: 'object',
|
|
1579
|
+
properties: {
|
|
1580
|
+
project: { type: 'string', description: 'Project name filter. Omit for all agents.' },
|
|
1581
|
+
},
|
|
1582
|
+
},
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
name: 'mio.agent.report',
|
|
1586
|
+
description: 'Performance report for one or all agents, aggregating task outcomes, memory, and experience reuse.',
|
|
1587
|
+
inputSchema: {
|
|
1588
|
+
type: 'object',
|
|
1589
|
+
properties: {
|
|
1590
|
+
agentId: { type: 'string', description: 'Agent to report on. Omit for all agents.' },
|
|
1591
|
+
project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
|
|
1592
|
+
},
|
|
1593
|
+
},
|
|
1594
|
+
},
|
|
1595
|
+
{
|
|
1596
|
+
name: 'mio.evolution.report',
|
|
1597
|
+
description: 'Generate cross-agent evolution report: ecosystem summary, agent performance comparison, cross-agent patterns, memory health, and optimization suggestions (ADR-017 Evolution plane).',
|
|
1598
|
+
inputSchema: {
|
|
1599
|
+
type: 'object',
|
|
1600
|
+
properties: {
|
|
1601
|
+
project: { type: 'string', description: 'Project name filter. Defaults to current repository.' },
|
|
1602
|
+
period: { type: 'string', description: 'Time window: 24h, 7d, 30d, or all (default).' },
|
|
1603
|
+
},
|
|
1604
|
+
},
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
name: 'mio.task.record_outcome',
|
|
1608
|
+
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.',
|
|
1609
|
+
inputSchema: {
|
|
1610
|
+
type: 'object',
|
|
1611
|
+
properties: {
|
|
1612
|
+
outcome: { type: 'string', description: 'Task outcome: success, failure, or aborted.' },
|
|
1613
|
+
task: { type: 'string', description: 'Brief task description.' },
|
|
1614
|
+
summary: { type: 'string', description: 'Optional task summary.' },
|
|
1615
|
+
verification: { type: 'string', description: 'Optional verification details.' },
|
|
1616
|
+
agentId: { type: 'string', description: 'Agent identifier. Defaults to MIO_CONTEXT agentId.' },
|
|
1617
|
+
project: { type: 'string', description: 'Project name. Defaults to current repository.' },
|
|
1618
|
+
traceId: { type: 'string', description: 'Optional custom trace ID. Auto-generated if omitted.' },
|
|
1619
|
+
},
|
|
1620
|
+
required: ['outcome'],
|
|
1621
|
+
},
|
|
1622
|
+
},
|
|
1474
1623
|
]
|
|
1475
1624
|
|
|
1476
1625
|
async function callTool(name, args = {}) {
|
|
@@ -1503,6 +1652,16 @@ async function callTool(name, args = {}) {
|
|
|
1503
1652
|
return phase0Report(args)
|
|
1504
1653
|
case 'mio.task.route':
|
|
1505
1654
|
return routeTask(args)
|
|
1655
|
+
case 'mio.agent.register':
|
|
1656
|
+
return registerAgent(args)
|
|
1657
|
+
case 'mio.agent.list':
|
|
1658
|
+
return listAgents(args)
|
|
1659
|
+
case 'mio.agent.report':
|
|
1660
|
+
return reportAgent(args)
|
|
1661
|
+
case 'mio.evolution.report':
|
|
1662
|
+
return evolutionReport(args)
|
|
1663
|
+
case 'mio.task.record_outcome':
|
|
1664
|
+
return recordTaskOutcome(args)
|
|
1506
1665
|
default:
|
|
1507
1666
|
throw new Error(`Unknown tool: ${name}`)
|
|
1508
1667
|
}
|
|
@@ -1584,4 +1743,242 @@ rl.on('close', () => {
|
|
|
1584
1743
|
}
|
|
1585
1744
|
})
|
|
1586
1745
|
|
|
1746
|
+
|
|
1747
|
+
function evolutionReport(args = {}) {
|
|
1748
|
+
const project = args.project || projectName()
|
|
1749
|
+
const period = String(args.period || 'all').trim()
|
|
1750
|
+
const now = Date.now()
|
|
1751
|
+
const periodMs = period === '24h' ? 86400000 : period === '7d' ? 604800000 : period === '30d' ? 2592000000 : Infinity
|
|
1752
|
+
const cutoff = periodMs === Infinity ? 0 : now - periodMs
|
|
1753
|
+
|
|
1754
|
+
const memories = readJsonl(memoryPath).filter((m) => !project || m.project === project || !m.project)
|
|
1755
|
+
const traces = readJsonl(tracePath).filter((t) => !project || t.project === project)
|
|
1756
|
+
const reuses = readJsonl(experienceReusePath).filter((r) => !project || r.project === project)
|
|
1757
|
+
const agents = readJsonl(agentsPath).filter((a) => !project || a.project === project)
|
|
1758
|
+
const queries = readJsonl(queryPath).filter((q) => !project || q.project === project)
|
|
1759
|
+
|
|
1760
|
+
const filteredTraces = cutoff ? traces.filter((t) => new Date(t.timestamp).getTime() >= cutoff) : traces
|
|
1761
|
+
const filteredMemories = cutoff ? memories.filter((m) => new Date(m.timestamp).getTime() >= cutoff) : memories
|
|
1762
|
+
|
|
1763
|
+
// Ecosystem summary
|
|
1764
|
+
const agentIds = new Set()
|
|
1765
|
+
filteredTraces.forEach((t) => { if (t.agent) agentIds.add(t.agent) })
|
|
1766
|
+
agents.forEach((a) => agentIds.add(a.agentId))
|
|
1767
|
+
const outcomes = filteredTraces.filter((t) => t.event_type === 'task_outcome')
|
|
1768
|
+
const successes = outcomes.filter((t) => t.outcome === 'success').length
|
|
1769
|
+
const failures = outcomes.filter((t) => t.outcome === 'failure').length
|
|
1770
|
+
const verifiedReuses = reuses.filter((r) => r.confirmed && r.reuse && r.behaviorChanged && r.outcomeImproved)
|
|
1771
|
+
const pendingReuses = reuses.filter((r) => r.source === 'auto_claim' && !r.confirmed)
|
|
1772
|
+
|
|
1773
|
+
const ecosystem = {
|
|
1774
|
+
agents: agentIds.size,
|
|
1775
|
+
totalTasks: outcomes.length,
|
|
1776
|
+
successRate: outcomes.length > 0 ? Math.round((successes / outcomes.length) * 100) : 0,
|
|
1777
|
+
totalMemories: filteredMemories.length,
|
|
1778
|
+
totalReuses: reuses.length,
|
|
1779
|
+
verifiedReuses: verifiedReuses.length,
|
|
1780
|
+
pendingReuses: pendingReuses.length,
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
// Agent performance comparison
|
|
1784
|
+
const agentPerf = []
|
|
1785
|
+
for (const agentId of agentIds) {
|
|
1786
|
+
const agentTraces = filteredTraces.filter((t) => t.agent === agentId)
|
|
1787
|
+
const agentOutcomes = agentTraces.filter((t) => t.event_type === 'task_outcome')
|
|
1788
|
+
const agentSuccesses = agentOutcomes.filter((t) => t.outcome === 'success').length
|
|
1789
|
+
const agentFailures = agentOutcomes.filter((t) => t.outcome === 'failure').length
|
|
1790
|
+
const agentMemories = filteredMemories.filter((m) => m.source === agentId)
|
|
1791
|
+
const agentVerified = reuses.filter((r) =>
|
|
1792
|
+
(r.sourceAgent === agentId || r.targetAgent === agentId) && r.confirmed && r.reuse && r.behaviorChanged && r.outcomeImproved
|
|
1793
|
+
)
|
|
1794
|
+
const reg = agents.find((a) => a.agentId === agentId)
|
|
1795
|
+
agentPerf.push({
|
|
1796
|
+
agentId,
|
|
1797
|
+
tasks: {
|
|
1798
|
+
total: agentOutcomes.length,
|
|
1799
|
+
success: agentSuccesses,
|
|
1800
|
+
failure: agentFailures,
|
|
1801
|
+
successRate: agentOutcomes.length > 0 ? Math.round((agentSuccesses / agentOutcomes.length) * 100) : 0,
|
|
1802
|
+
},
|
|
1803
|
+
memories: agentMemories.length,
|
|
1804
|
+
verifiedReuses: agentVerified.length,
|
|
1805
|
+
registered: !!reg,
|
|
1806
|
+
lastActive: reg ? reg.lastSeenAt : (agentTraces.length > 0 ? agentTraces[agentTraces.length - 1].timestamp : null),
|
|
1807
|
+
})
|
|
1808
|
+
}
|
|
1809
|
+
agentPerf.sort((a, b) => b.tasks.successRate - a.tasks.successRate || b.tasks.total - a.tasks.total)
|
|
1810
|
+
|
|
1811
|
+
// Cross-agent patterns
|
|
1812
|
+
const crossPatterns = []
|
|
1813
|
+
const reuseByPair = {}
|
|
1814
|
+
for (const r of reuses) {
|
|
1815
|
+
const key = r.sourceAgent + '->' + r.targetAgent
|
|
1816
|
+
if (!reuseByPair[key]) reuseByPair[key] = { source: r.sourceAgent, target: r.targetAgent, total: 0, verified: 0, improved: 0 }
|
|
1817
|
+
reuseByPair[key].total++
|
|
1818
|
+
if (r.confirmed) reuseByPair[key].verified++
|
|
1819
|
+
if (r.outcomeImproved) reuseByPair[key].improved++
|
|
1820
|
+
}
|
|
1821
|
+
for (const [key, pair] of Object.entries(reuseByPair)) {
|
|
1822
|
+
if (pair.source !== pair.target) {
|
|
1823
|
+
crossPatterns.push({
|
|
1824
|
+
type: 'reuse_path',
|
|
1825
|
+
from: pair.source,
|
|
1826
|
+
to: pair.target,
|
|
1827
|
+
total: pair.total,
|
|
1828
|
+
verified: pair.verified,
|
|
1829
|
+
improved: pair.improved,
|
|
1830
|
+
})
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
crossPatterns.sort((a, b) => b.verified - a.verified || b.total - a.total)
|
|
1834
|
+
|
|
1835
|
+
// Memory health
|
|
1836
|
+
const byKind = {}
|
|
1837
|
+
let shortContent = 0
|
|
1838
|
+
let missingKind = 0
|
|
1839
|
+
for (const m of filteredMemories) {
|
|
1840
|
+
const kind = m.kind || 'unknown'
|
|
1841
|
+
byKind[kind] = (byKind[kind] || 0) + 1
|
|
1842
|
+
const content = String(m.content || '').trim()
|
|
1843
|
+
if (content.length < 20) shortContent++
|
|
1844
|
+
if (!m.kind) missingKind++
|
|
1845
|
+
}
|
|
1846
|
+
const memoryHealth = {
|
|
1847
|
+
total: filteredMemories.length,
|
|
1848
|
+
byKind,
|
|
1849
|
+
shortContent,
|
|
1850
|
+
missingKind,
|
|
1851
|
+
qualityScore: filteredMemories.length > 0
|
|
1852
|
+
? Math.round(((filteredMemories.length - shortContent - missingKind) / filteredMemories.length) * 100)
|
|
1853
|
+
: 100,
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// Suggestions engine
|
|
1857
|
+
const suggestions = []
|
|
1858
|
+
|
|
1859
|
+
// Routing suggestion: compare agent success rates
|
|
1860
|
+
if (agentPerf.length >= 2) {
|
|
1861
|
+
const best = agentPerf[0]
|
|
1862
|
+
const worst = agentPerf[agentPerf.length - 1]
|
|
1863
|
+
if (best.tasks.total >= 5 && worst.tasks.total >= 5 && best.tasks.successRate - worst.tasks.successRate >= 15) {
|
|
1864
|
+
suggestions.push({
|
|
1865
|
+
category: 'routing',
|
|
1866
|
+
priority: 'high',
|
|
1867
|
+
description: best.agentId + ' has ' + best.tasks.successRate + '% success rate vs ' + worst.agentId + ' at ' + worst.tasks.successRate + '%; prefer routing to ' + best.agentId,
|
|
1868
|
+
basedOn: 'task_outcome comparison',
|
|
1869
|
+
})
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
// Pending auto-claim suggestion
|
|
1874
|
+
if (pendingReuses.length >= 10) {
|
|
1875
|
+
suggestions.push({
|
|
1876
|
+
category: 'evidence',
|
|
1877
|
+
priority: 'medium',
|
|
1878
|
+
description: pendingReuses.length + ' pending auto-claims awaiting confirmation; review cross-agent claims to increase verified reuse count',
|
|
1879
|
+
basedOn: 'experience_reuse.jsonl',
|
|
1880
|
+
})
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
// Memory quality suggestion
|
|
1884
|
+
if (memoryHealth.qualityScore < 80 && filteredMemories.length > 10) {
|
|
1885
|
+
suggestions.push({
|
|
1886
|
+
category: 'memory',
|
|
1887
|
+
priority: 'medium',
|
|
1888
|
+
description: 'Memory quality score is ' + memoryHealth.qualityScore + '%; ' + shortContent + ' short records and ' + missingKind + ' missing kind fields found',
|
|
1889
|
+
basedOn: 'memory.jsonl quality analysis',
|
|
1890
|
+
})
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
// Cross-agent value suggestion
|
|
1894
|
+
if (crossPatterns.length === 0 && agentIds.size >= 2) {
|
|
1895
|
+
suggestions.push({
|
|
1896
|
+
category: 'interop',
|
|
1897
|
+
priority: 'high',
|
|
1898
|
+
description: 'Multiple agents registered but no cross-agent reuse patterns detected; ensure agents query shared memory',
|
|
1899
|
+
basedOn: 'experience_reuse.jsonl',
|
|
1900
|
+
})
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
// Active agent suggestion
|
|
1904
|
+
const inactiveAgents = agentPerf.filter((a) => {
|
|
1905
|
+
if (!a.lastActive) return false
|
|
1906
|
+
return (now - new Date(a.lastActive).getTime()) > 7 * 86400000
|
|
1907
|
+
})
|
|
1908
|
+
if (inactiveAgents.length > 0) {
|
|
1909
|
+
suggestions.push({
|
|
1910
|
+
category: 'coverage',
|
|
1911
|
+
priority: 'low',
|
|
1912
|
+
description: inactiveAgents.map((a) => a.agentId).join(', ') + ' inactive for >7 days; consider removal or reactivation',
|
|
1913
|
+
basedOn: 'agent registry lastSeenAt',
|
|
1914
|
+
})
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
suggestions.sort((a, b) => {
|
|
1918
|
+
const p = { high: 0, medium: 1, low: 2 }
|
|
1919
|
+
return (p[a.priority] || 2) - (p[b.priority] || 2)
|
|
1920
|
+
})
|
|
1921
|
+
|
|
1922
|
+
return {
|
|
1923
|
+
project,
|
|
1924
|
+
generatedAt: new Date().toISOString(),
|
|
1925
|
+
period,
|
|
1926
|
+
ecosystem,
|
|
1927
|
+
agentPerformance: agentPerf,
|
|
1928
|
+
crossAgentPatterns: crossPatterns,
|
|
1929
|
+
memoryHealth,
|
|
1930
|
+
suggestions,
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
|
|
1935
|
+
function recordTaskOutcome(args = {}) {
|
|
1936
|
+
const outcome = String(args.outcome || '').trim().toLowerCase()
|
|
1937
|
+
if (!['success', 'failure', 'aborted'].includes(outcome)) {
|
|
1938
|
+
throw new Error('task.record_outcome requires outcome to be success, failure, or aborted')
|
|
1939
|
+
}
|
|
1940
|
+
const agentId = String(args.agentId || runtimeAgentId() || 'unknown').trim()
|
|
1941
|
+
const project = args.project || projectName()
|
|
1942
|
+
const task = String(args.task || '').trim()
|
|
1943
|
+
const summary = String(args.summary || '').trim()
|
|
1944
|
+
const verification = String(args.verification || '').trim()
|
|
1945
|
+
const traceId = String(args.traceId || '').trim() || createId('trace:' + agentId + ':' + project)
|
|
1946
|
+
|
|
1947
|
+
// 1. Record trace event
|
|
1948
|
+
const event = {
|
|
1949
|
+
id: createId('trace'),
|
|
1950
|
+
timestamp: new Date().toISOString(),
|
|
1951
|
+
trace_id: traceId,
|
|
1952
|
+
event_type: 'task_outcome',
|
|
1953
|
+
outcome,
|
|
1954
|
+
payload: { task, summary, verification },
|
|
1955
|
+
agent: agentId,
|
|
1956
|
+
host: 'mcp',
|
|
1957
|
+
project,
|
|
1958
|
+
}
|
|
1959
|
+
appendJsonl(tracePath, event)
|
|
1960
|
+
|
|
1961
|
+
// 2. Auto-claim experience reuse
|
|
1962
|
+
const autoClaims = autoClaimExperienceReuse(event)
|
|
1963
|
+
|
|
1964
|
+
// 3. Update agent registry
|
|
1965
|
+
const now = new Date().toISOString()
|
|
1966
|
+
const agents = readJsonl(agentsPath)
|
|
1967
|
+
const agent = agents.find((a) => a.agentId === agentId && a.project === project)
|
|
1968
|
+
if (agent) {
|
|
1969
|
+
agent.lastSeenAt = now
|
|
1970
|
+
agent.taskCount = (agent.taskCount || 0) + 1
|
|
1971
|
+
agent.successCount = (agent.successCount || 0) + (outcome === 'success' ? 1 : 0)
|
|
1972
|
+
agent.failureCount = (agent.failureCount || 0) + (outcome === 'failure' ? 1 : 0)
|
|
1973
|
+
writeJsonl(agentsPath, agents)
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
return {
|
|
1977
|
+
recorded: true,
|
|
1978
|
+
event: { id: event.id, trace_id: traceId, outcome, agent: agentId, project },
|
|
1979
|
+
agentUpdated: !!agent,
|
|
1980
|
+
autoClaims: autoClaims.length > 0 ? autoClaims : undefined,
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1587
1984
|
module.exports = { TOOLS, callTool, handleMessage, readJsonl, appendJsonl, rl }
|