mio-agent-runtime 0.5.16 → 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mio-agent-runtime",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.17",
|
|
4
4
|
"description": "Mio Agent Runtime installer and MCP intelligence layer",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -9,28 +9,30 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"bin/",
|
|
12
|
-
"server/",
|
|
13
|
-
"adapters/",
|
|
14
|
-
"observe/",
|
|
15
|
-
"scripts/",
|
|
16
|
-
"README.md",
|
|
12
|
+
"server/",
|
|
13
|
+
"adapters/",
|
|
14
|
+
"observe/",
|
|
15
|
+
"scripts/",
|
|
16
|
+
"README.md",
|
|
17
17
|
"!server/mio-intelligence-mcp/__tests__"
|
|
18
18
|
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"check": "node --check bin/mio.js && node --check adapters/host-utils.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check adapters/hermes.js && node --check server/runtime-modules.js && node --check server/host-capabilities.js && node --check server/evolution-cutover.js && node --check server/mio-intelligence-mcp/index.js && node --check server/mio-intelligence-mcp/phase0.js && node --check observe/observer.js && node --check scripts/verify-packed-runtime.js",
|
|
21
|
-
"prepack": "npm run check",
|
|
22
|
-
"verify:pack-install": "node scripts/verify-packed-runtime.js"
|
|
23
|
-
},
|
|
24
|
-
"engines": {
|
|
25
|
-
"node": ">=18"
|
|
26
|
-
},
|
|
27
|
-
"dependencies": {
|
|
28
|
-
"@akemi-mio/runtime-contracts": "^0.1.0",
|
|
29
|
-
"@akemi-mio/runtime-foundation": "^0.1.0",
|
|
30
|
-
"@akemi-mio/experience-memory": "^0.1.0",
|
|
31
|
-
"@akemi-mio/evolution-learning": "^0.1.0",
|
|
32
|
-
"@akemi-mio/evolution-strategy": "^0.1.0",
|
|
33
|
-
"@akemi-mio/evolution-safety": "^0.1.0",
|
|
34
|
-
"@akemi-mio/evolution-scheduler": "^0.1.0"
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
"scripts": {
|
|
20
|
+
"check": "node --check bin/mio.js && node --check adapters/host-utils.js && node --check adapters/codex.js && node --check adapters/opencode.js && node --check adapters/workbuddy.js && node --check adapters/hermes.js && node --check server/runtime-modules.js && node --check server/host-capabilities.js && node --check server/evolution-cutover.js && node --check server/mio-intelligence-mcp/index.js && node --check server/mio-intelligence-mcp/phase0.js && node --check observe/observer.js && node --check scripts/verify-packed-runtime.js",
|
|
21
|
+
"prepack": "npm run check",
|
|
22
|
+
"verify:pack-install": "node scripts/verify-packed-runtime.js"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@akemi-mio/runtime-contracts": "^0.1.0",
|
|
29
|
+
"@akemi-mio/runtime-foundation": "^0.1.0",
|
|
30
|
+
"@akemi-mio/experience-memory": "^0.1.0",
|
|
31
|
+
"@akemi-mio/evolution-learning": "^0.1.0",
|
|
32
|
+
"@akemi-mio/evolution-strategy": "^0.1.0",
|
|
33
|
+
"@akemi-mio/evolution-safety": "^0.1.0",
|
|
34
|
+
"@akemi-mio/evolution-scheduler": "^0.1.0",
|
|
35
|
+
"@akemi-mio/observer": "^0.1.0",
|
|
36
|
+
"@akemi-mio/insight": "^0.1.0"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const crypto = require('crypto')
|
|
6
|
+
|
|
7
|
+
// ═══════════════════════════════════════════════
|
|
8
|
+
// JSONL Store — ideas, combos, experiments
|
|
9
|
+
// ═══════════════════════════════════════════════
|
|
10
|
+
|
|
11
|
+
class CreativityStore {
|
|
12
|
+
constructor(dataDir) {
|
|
13
|
+
this.dataDir = dataDir
|
|
14
|
+
this.hypothesesPath = path.join(dataDir, 'creativity-hypotheses.jsonl')
|
|
15
|
+
this.combosPath = path.join(dataDir, 'creativity-combos.jsonl')
|
|
16
|
+
this.experimentsPath = path.join(dataDir, 'creativity-experiments.jsonl')
|
|
17
|
+
this._hypotheses = null
|
|
18
|
+
this._combos = null
|
|
19
|
+
this._experiments = null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
_ensureDir() {
|
|
23
|
+
if (!fs.existsSync(this.dataDir)) {
|
|
24
|
+
fs.mkdirSync(this.dataDir, { recursive: true })
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
_readJsonl(filePath) {
|
|
29
|
+
if (!fs.existsSync(filePath)) return []
|
|
30
|
+
return fs.readFileSync(filePath, 'utf-8')
|
|
31
|
+
.split('\n')
|
|
32
|
+
.filter(Boolean)
|
|
33
|
+
.map(line => {
|
|
34
|
+
try { return JSON.parse(line) } catch { return null }
|
|
35
|
+
})
|
|
36
|
+
.filter(Boolean)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_appendJsonl(filePath, record) {
|
|
40
|
+
this._ensureDir()
|
|
41
|
+
fs.appendFileSync(filePath, JSON.stringify(record) + '\n')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getHypotheses(opts = {}) {
|
|
45
|
+
if (this._hypotheses === null) {
|
|
46
|
+
this._hypotheses = this._readJsonl(this.hypothesesPath)
|
|
47
|
+
}
|
|
48
|
+
let list = this._hypotheses
|
|
49
|
+
if (opts.status) list = list.filter(h => h.status === opts.status)
|
|
50
|
+
if (opts.limit) list = list.slice(-opts.limit)
|
|
51
|
+
return list
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
addHypothesis(h) {
|
|
55
|
+
this._appendJsonl(this.hypothesesPath, h)
|
|
56
|
+
if (this._hypotheses) this._hypotheses.push(h)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
updateHypothesis(id, patch) {
|
|
60
|
+
const all = this.getHypotheses()
|
|
61
|
+
const idx = all.findIndex(h => h.id === id)
|
|
62
|
+
if (idx === -1) return false
|
|
63
|
+
Object.assign(all[idx], patch)
|
|
64
|
+
this._hypotheses = all
|
|
65
|
+
this._rewriteFile(this.hypothesesPath, all)
|
|
66
|
+
return true
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getCombos(limit) {
|
|
70
|
+
if (this._combos === null) {
|
|
71
|
+
this._combos = this._readJsonl(this.combosPath)
|
|
72
|
+
}
|
|
73
|
+
return limit ? this._combos.slice(-limit) : this._combos
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
addCombo(c) {
|
|
77
|
+
this._appendJsonl(this.combosPath, c)
|
|
78
|
+
if (this._combos) this._combos.push(c)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
getExperiments() {
|
|
82
|
+
if (this._experiments === null) {
|
|
83
|
+
this._experiments = this._readJsonl(this.experimentsPath)
|
|
84
|
+
}
|
|
85
|
+
return this._experiments
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
addExperiment(e) {
|
|
89
|
+
this._appendJsonl(this.experimentsPath, e)
|
|
90
|
+
if (this._experiments) this._experiments.push(e)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
count() {
|
|
94
|
+
return {
|
|
95
|
+
hypotheses: this.getHypotheses().length,
|
|
96
|
+
combos: this.getCombos().length,
|
|
97
|
+
experiments: this.getExperiments().length,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
_rewriteFile(filePath, records) {
|
|
102
|
+
this._ensureDir()
|
|
103
|
+
fs.writeFileSync(filePath, records.map(r => JSON.stringify(r)).join('\n') + '\n')
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ═══════════════════════════════════════════════
|
|
108
|
+
// Concept Mixer — pair sources for novel combos
|
|
109
|
+
// ═══════════════════════════════════════════════
|
|
110
|
+
|
|
111
|
+
function mulberry32(seed) {
|
|
112
|
+
let t = seed | 0
|
|
113
|
+
return () => {
|
|
114
|
+
t = (t + 0x6D2B79F5) | 0
|
|
115
|
+
let x = Math.imul(t ^ (t >>> 15), 1 | t)
|
|
116
|
+
x ^= x + Math.imul(x ^ (x >>> 7), 61 | x)
|
|
117
|
+
return ((x ^ (x >>> 14)) >>> 0) / 4294967296
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function mixConcepts(sources, exploredPairs, rng) {
|
|
122
|
+
if (sources.length < 2) return []
|
|
123
|
+
const pairs = []
|
|
124
|
+
for (let i = 0; i < sources.length; i++) {
|
|
125
|
+
for (let j = i + 1; j < sources.length; j++) {
|
|
126
|
+
const key = `${sources[i].name}||${sources[j].name}`
|
|
127
|
+
const reverseKey = `${sources[j].name}||${sources[i].name}`
|
|
128
|
+
if (exploredPairs.has(key) || exploredPairs.has(reverseKey)) continue
|
|
129
|
+
pairs.push([sources[i], sources[j]])
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Shuffle and take top pairs
|
|
133
|
+
for (let i = pairs.length - 1; i > 0; i--) {
|
|
134
|
+
const j = Math.floor(rng() * (i + 1))
|
|
135
|
+
;[pairs[i], pairs[j]] = [pairs[j], pairs[i]]
|
|
136
|
+
}
|
|
137
|
+
return pairs.slice(0, Math.min(5, pairs.length))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ═══════════════════════════════════════════════
|
|
141
|
+
// Novelty Scorer — reject duplicates
|
|
142
|
+
// ═══════════════════════════════════════════════
|
|
143
|
+
|
|
144
|
+
function bigramSet(text) {
|
|
145
|
+
const s = new Set()
|
|
146
|
+
const lower = text.toLowerCase()
|
|
147
|
+
for (let i = 0; i < lower.length - 1; i++) {
|
|
148
|
+
s.add(lower.slice(i, i + 2))
|
|
149
|
+
}
|
|
150
|
+
return s
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function jaccard(a, b) {
|
|
154
|
+
if (a.size === 0 && b.size === 0) return 1
|
|
155
|
+
let inter = 0
|
|
156
|
+
for (const x of a) if (b.has(x)) inter++
|
|
157
|
+
return inter / (a.size + b.size - inter)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function evaluateNovelty(idea, recent, rejected) {
|
|
161
|
+
const ideaBigrams = bigramSet(idea.title + ' ' + idea.idea)
|
|
162
|
+
for (const r of recent) {
|
|
163
|
+
const sim = jaccard(ideaBigrams, bigramSet(r.title + ' ' + r.idea))
|
|
164
|
+
if (sim > 0.7) {
|
|
165
|
+
return { shouldReject: true, rejectReason: `too similar to recent: "${r.title}" (sim=${sim.toFixed(2)})`, adjustedNovelty: idea.novelty }
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
for (const r of rejected) {
|
|
169
|
+
const sim = jaccard(ideaBigrams, bigramSet(r.title + ' ' + r.idea))
|
|
170
|
+
if (sim > 0.6) {
|
|
171
|
+
return { shouldReject: true, rejectReason: `matches rejected: "${r.title}" (sim=${sim.toFixed(2)})`, adjustedNovelty: idea.novelty }
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return { shouldReject: false, adjustedNovelty: Math.min(100, idea.novelty + 5) }
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// ═══════════════════════════════════════════════
|
|
178
|
+
// Creativity Engine — main orchestrator
|
|
179
|
+
// ═══════════════════════════════════════════════
|
|
180
|
+
|
|
181
|
+
const STRATEGY_CYCLE = ['explore', 'signal', 'stable']
|
|
182
|
+
|
|
183
|
+
class CreativityEngine {
|
|
184
|
+
constructor(dataDir, chatJson) {
|
|
185
|
+
this.store = new CreativityStore(dataDir)
|
|
186
|
+
this.chatJson = chatJson
|
|
187
|
+
this.strategyIndex = 0
|
|
188
|
+
this.exploredPairs = new Set()
|
|
189
|
+
this.rng = mulberry32(Date.now())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async generate(sourceLabels, strategy) {
|
|
193
|
+
const sources = sourceLabels.map(s => ({
|
|
194
|
+
name: s.name || 'unnamed',
|
|
195
|
+
content: s.content || '',
|
|
196
|
+
type: s.type || 'knowledge',
|
|
197
|
+
weight: s.weight || 0.5,
|
|
198
|
+
}))
|
|
199
|
+
|
|
200
|
+
if (sources.length < 2) {
|
|
201
|
+
return { ideas: [], reason: 'need at least 2 sources' }
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const strat = strategy || STRATEGY_CYCLE[this.strategyIndex % STRATEGY_CYCLE.length]
|
|
205
|
+
this.strategyIndex++
|
|
206
|
+
|
|
207
|
+
// Mix concepts
|
|
208
|
+
const recent = this.store.getHypotheses({ limit: 30 })
|
|
209
|
+
const rejected = this.store.getHypotheses({ status: 'rejected', limit: 50 })
|
|
210
|
+
const exploredPairs = new Set(recent.flatMap(h => (h.sourceLabels || []).map((l, i, a) => i > 0 ? `${a[i-1]}||${l}` : '')).filter(Boolean))
|
|
211
|
+
const pairs = mixConcepts(sources, exploredPairs, this.rng.bind(this))
|
|
212
|
+
|
|
213
|
+
if (pairs.length === 0) {
|
|
214
|
+
return { ideas: [], reason: 'all pairs already explored' }
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Generate hypotheses via LLM
|
|
218
|
+
const ideas = []
|
|
219
|
+
for (const [a, b] of pairs) {
|
|
220
|
+
const prompt = this._buildGenerationPrompt(a, b, strat, recent.slice(-10), rejected.slice(-5))
|
|
221
|
+
try {
|
|
222
|
+
const result = await this.chatJson(prompt, {
|
|
223
|
+
system: `You are a creative hypothesis generator. Output JSON with fields: title, idea, expectedBenefit, risk, novelty (0-100), feasibility (0-100), impact (0-100). Generate ONE novel hypothesis by combining the given concepts.`,
|
|
224
|
+
temperature: strat === 'explore' ? 0.8 : 0.4,
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
if (result.data) {
|
|
228
|
+
const h = {
|
|
229
|
+
id: crypto.randomUUID(),
|
|
230
|
+
title: result.data.title || 'Untitled',
|
|
231
|
+
idea: result.data.idea || '',
|
|
232
|
+
expectedBenefit: result.data.expectedBenefit || '',
|
|
233
|
+
risk: result.data.risk || '',
|
|
234
|
+
novelty: Math.min(100, Math.max(0, result.data.novelty || 50)),
|
|
235
|
+
feasibility: Math.min(100, Math.max(0, result.data.feasibility || 50)),
|
|
236
|
+
impact: Math.min(100, Math.max(0, result.data.impact || 50)),
|
|
237
|
+
sourceLabels: [a.name, b.name],
|
|
238
|
+
status: 'active',
|
|
239
|
+
createdAt: Date.now(),
|
|
240
|
+
strategy: strat,
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Novelty check
|
|
244
|
+
const verdict = evaluateNovelty(h, recent, rejected)
|
|
245
|
+
if (verdict.shouldReject) {
|
|
246
|
+
h.status = 'rejected'
|
|
247
|
+
h.rejectionReason = verdict.rejectReason
|
|
248
|
+
} else {
|
|
249
|
+
h.novelty = verdict.adjustedNovelty
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
this.store.addHypothesis(h)
|
|
253
|
+
ideas.push(h)
|
|
254
|
+
|
|
255
|
+
// Record combo
|
|
256
|
+
this.store.addCombo({
|
|
257
|
+
id: crypto.randomUUID(),
|
|
258
|
+
sources: [a.name, b.name],
|
|
259
|
+
description: h.title,
|
|
260
|
+
createdAt: Date.now(),
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
} catch (err) {
|
|
264
|
+
// LLM call failed, skip this pair
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { ideas, strategy: strat, pairsAttempted: pairs.length }
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async ferment(limit = 5) {
|
|
272
|
+
const fermentable = this.store.getHypotheses({ status: 'active' })
|
|
273
|
+
.concat(this.store.getHypotheses({ status: 'draft' }))
|
|
274
|
+
.slice(-limit)
|
|
275
|
+
|
|
276
|
+
if (fermentable.length === 0) {
|
|
277
|
+
return { fermented: 0, reason: 'no fermentable hypotheses' }
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const results = []
|
|
281
|
+
for (const h of fermentable) {
|
|
282
|
+
const prompt = `Review and refine this hypothesis. Current state:
|
|
283
|
+
Title: ${h.title}
|
|
284
|
+
Idea: ${h.idea}
|
|
285
|
+
Novelty: ${h.novelty}, Feasibility: ${h.feasibility}, Impact: ${h.impact}
|
|
286
|
+
|
|
287
|
+
Output JSON with: title, idea, expectedBenefit, risk, novelty, feasibility, impact, verdict (promote/keep/reject/merge), reason.`
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const result = await this.chatJson(prompt, {
|
|
291
|
+
system: 'You are a creative hypothesis reviewer. Output JSON with refined scores and a verdict.',
|
|
292
|
+
temperature: 0.3,
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
if (result.data) {
|
|
296
|
+
const patch = {
|
|
297
|
+
title: result.data.title || h.title,
|
|
298
|
+
idea: result.data.idea || h.idea,
|
|
299
|
+
expectedBenefit: result.data.expectedBenefit || h.expectedBenefit,
|
|
300
|
+
risk: result.data.risk || h.risk,
|
|
301
|
+
novelty: Math.min(100, Math.max(0, result.data.novelty || h.novelty)),
|
|
302
|
+
feasibility: Math.min(100, Math.max(0, result.data.feasibility || h.feasibility)),
|
|
303
|
+
impact: Math.min(100, Math.max(0, result.data.impact || h.impact)),
|
|
304
|
+
fermentedAt: Date.now(),
|
|
305
|
+
fermentCount: (h.fermentCount || 0) + 1,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (result.data.verdict === 'reject') {
|
|
309
|
+
patch.status = 'rejected'
|
|
310
|
+
} else if (result.data.verdict === 'promote' && (h.novelty + h.feasibility + h.impact) > 200) {
|
|
311
|
+
patch.status = 'validated'
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.store.updateHypothesis(h.id, patch)
|
|
315
|
+
results.push({ id: h.id, title: h.title, verdict: result.data.verdict, reason: result.data.reason })
|
|
316
|
+
}
|
|
317
|
+
} catch (err) {
|
|
318
|
+
// skip
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return { fermented: results.length, results }
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
status() {
|
|
326
|
+
const count = this.store.count()
|
|
327
|
+
const active = this.store.getHypotheses({ status: 'active' })
|
|
328
|
+
const validated = this.store.getHypotheses({ status: 'validated' })
|
|
329
|
+
const rejected = this.store.getHypotheses({ status: 'rejected' })
|
|
330
|
+
|
|
331
|
+
return {
|
|
332
|
+
...count,
|
|
333
|
+
active: active.length,
|
|
334
|
+
validated: validated.length,
|
|
335
|
+
rejected: rejected.length,
|
|
336
|
+
recentIdeas: active.slice(-5).map(h => ({
|
|
337
|
+
id: h.id,
|
|
338
|
+
title: h.title,
|
|
339
|
+
novelty: h.novelty,
|
|
340
|
+
feasibility: h.feasibility,
|
|
341
|
+
impact: h.impact,
|
|
342
|
+
score: h.novelty + h.feasibility + h.impact,
|
|
343
|
+
})),
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
list(opts = {}) {
|
|
348
|
+
return this.store.getHypotheses(opts).map(h => ({
|
|
349
|
+
id: h.id,
|
|
350
|
+
title: h.title,
|
|
351
|
+
idea: h.idea,
|
|
352
|
+
status: h.status,
|
|
353
|
+
novelty: h.novelty,
|
|
354
|
+
feasibility: h.feasibility,
|
|
355
|
+
impact: h.impact,
|
|
356
|
+
score: h.novelty + h.feasibility + h.impact,
|
|
357
|
+
sourceLabels: h.sourceLabels,
|
|
358
|
+
createdAt: h.createdAt,
|
|
359
|
+
fermentedAt: h.fermentedAt,
|
|
360
|
+
fermentCount: h.fermentCount,
|
|
361
|
+
}))
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
_buildGenerationPrompt(a, b, strategy, recentHypotheses, rejectedHypotheses) {
|
|
365
|
+
let prompt = `Combine these two concepts into ONE novel hypothesis:\n\n`
|
|
366
|
+
prompt += `Concept A: "${a.name}" — ${a.content}\n`
|
|
367
|
+
prompt += `Concept B: "${b.name}" — ${b.content}\n\n`
|
|
368
|
+
prompt += `Strategy: ${strategy}\n`
|
|
369
|
+
|
|
370
|
+
if (strategy === 'explore') {
|
|
371
|
+
prompt += `Cross-domain combination. Emphasize structural differences and unexpected connections.\n`
|
|
372
|
+
} else if (strategy === 'signal') {
|
|
373
|
+
prompt += `Inject external signals. Be provocative and challenge assumptions.\n`
|
|
374
|
+
} else {
|
|
375
|
+
prompt += `Stable combination. Conservative refinement of existing ideas.\n`
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (recentHypotheses.length > 0) {
|
|
379
|
+
prompt += `\nRecent ideas (avoid duplicates):\n`
|
|
380
|
+
for (const h of recentHypotheses) {
|
|
381
|
+
prompt += `- "${h.title}" (novelty=${h.novelty})\n`
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (rejectedHypotheses.length > 0) {
|
|
386
|
+
prompt += `\nRejected ideas (learn from failures):\n`
|
|
387
|
+
for (const h of rejectedHypotheses) {
|
|
388
|
+
prompt += `- "${h.title}" — ${h.risk}\n`
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
prompt += `\nOutput JSON: {"title": "...", "idea": "...", "expectedBenefit": "...", "risk": "...", "novelty": 0-100, "feasibility": 0-100, "impact": 0-100}`
|
|
393
|
+
return prompt
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
module.exports = { CreativityEngine, CreativityStore }
|
|
@@ -103,7 +103,7 @@ task_outcome 数据经 auto-claim → confirm 后会自动升级后续同类动
|
|
|
103
103
|
```toml
|
|
104
104
|
[mcp_servers.mio-intelligence]
|
|
105
105
|
command = 'C:\nvm4w\nodejs\node.exe'
|
|
106
|
-
args = ['
|
|
106
|
+
args = ['/absolute/path/to/mio-agent-runtime/server/mio-intelligence-mcp/index.js']
|
|
107
107
|
startup_timeout_sec = 30
|
|
108
108
|
|
|
109
109
|
[mcp_servers.mio-intelligence.env]
|
|
@@ -136,7 +136,7 @@ OpenCode / WorkBuddy 的 MCP 配置只需保持同样语义:
|
|
|
136
136
|
"mio": {
|
|
137
137
|
"command": "node",
|
|
138
138
|
"args": [
|
|
139
|
-
"
|
|
139
|
+
"/absolute/path/to/mio-agent-runtime/server/mio-intelligence-mcp/index.js"
|
|
140
140
|
],
|
|
141
141
|
"env": {
|
|
142
142
|
"MIO_DATA_DIR": "C:/Users/gf191/.mio-intelligence",
|
|
@@ -10,9 +10,74 @@ const { getEvolutionStatus } = require('../runtime-modules.js')
|
|
|
10
10
|
const { listHostCapabilities } = require('../host-capabilities.js')
|
|
11
11
|
const { createEvolutionCutoverTools } = require('../evolution-cutover.js')
|
|
12
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 {}
|
|
34
|
+
|
|
13
35
|
const SERVER_INFO = { name: 'mio-intelligence-mcp', version: '0.1.0' }
|
|
14
36
|
const PROTOCOL_VERSION = '2024-11-05'
|
|
15
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
|
+
|
|
16
81
|
const dataDir = path.resolve(
|
|
17
82
|
process.env.MIO_DATA_DIR || path.join(process.cwd(), '.mio-intelligence')
|
|
18
83
|
)
|
|
@@ -25,8 +90,10 @@ const subscriptionPath = path.join(dataDir, 'subscriptions.jsonl')
|
|
|
25
90
|
const digestStatePath = path.join(dataDir, 'digest_state.json')
|
|
26
91
|
fs.mkdirSync(dataDir, { recursive: true })
|
|
27
92
|
|
|
28
|
-
const { loadPhase0, summarizePhase0, renderPhase0Markdown } = require('./phase0')
|
|
29
|
-
const evolutionCutover = createEvolutionCutoverTools({ dataDir, appendJsonl, readJsonl, projectName })
|
|
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
|
|
30
97
|
|
|
31
98
|
let runtimeContext = {}
|
|
32
99
|
try {
|
|
@@ -1720,8 +1787,410 @@ const TOOLS = [
|
|
|
1720
1787
|
required: ['outcome'],
|
|
1721
1788
|
},
|
|
1722
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
|
+
}] : []),
|
|
1723
1991
|
]
|
|
1724
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
|
+
|
|
1725
2194
|
async function callTool(name, args = {}) {
|
|
1726
2195
|
switch (name) {
|
|
1727
2196
|
case 'mio.memory.query':
|
|
@@ -1776,9 +2245,43 @@ async function callTool(name, args = {}) {
|
|
|
1776
2245
|
return evolutionCutover.authorityPlan(args)
|
|
1777
2246
|
case 'mio.evolution.cutover.apply':
|
|
1778
2247
|
return evolutionCutover.applyCutover(args)
|
|
1779
|
-
case 'mio.task.record_outcome':
|
|
1780
|
-
return recordTaskOutcome(args)
|
|
1781
|
-
|
|
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:
|
|
1782
2285
|
throw new Error(`Unknown tool: ${name}`)
|
|
1783
2286
|
}
|
|
1784
2287
|
}
|