mio-agent-runtime 0.13.3 → 0.13.5
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 +19 -5
- package/adapters/claude-code.js +6 -6
- package/adapters/codex.js +121 -121
- package/adapters/hermes.js +182 -176
- package/adapters/opencode.js +203 -176
- package/adapters/workbuddy.js +197 -196
- package/bin/mio.js +93 -12
- package/observe/observer.js +1876 -1867
- package/package.json +3 -3
- package/server/creativity-engine.js +238 -67
- package/server/evolution-cutover.js +5 -2
- package/server/insight-store.js +37 -11
- package/server/llm-client.js +29 -8
- package/server/mio-intelligence-mcp/README.md +173 -173
- package/server/mio-intelligence-mcp/index.js +27 -6
- package/server/mio-intelligence-mcp/package.json +17 -17
- package/server/mio-intelligence-mcp/phase0.js +365 -365
- package/server/observer-store.js +12 -11
- package/server/task-store.js +475 -475
package/adapters/workbuddy.js
CHANGED
|
@@ -1,196 +1,197 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const os = require('os')
|
|
6
|
-
const crypto = require('crypto')
|
|
7
|
-
|
|
8
|
-
function configDir() {
|
|
9
|
-
return (
|
|
10
|
-
process.env.WORKBUDDY_CONFIG_DIR ||
|
|
11
|
-
process.env.CODEBUDDY_CONFIG_DIR ||
|
|
12
|
-
path.join(os.homedir(), '.workbuddy')
|
|
13
|
-
)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function configPath() {
|
|
17
|
-
return path.join(configDir(), 'mcp.json')
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function legacyConfigPath() {
|
|
21
|
-
return path.join(configDir(), '.mcp.json')
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function approvalsPath() {
|
|
25
|
-
return path.join(configDir(), 'mcp-approvals.json')
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// User-level memory rules file. WorkBuddy's codebuddy CLI (a CodeBuddy fork)
|
|
29
|
-
// auto-loads CODEBUDDY.md (or CODEBUDDY.mdc) from the config dir into every
|
|
30
|
-
// session (MemoryLoader user scope -> system-reminder-md / prompt context).
|
|
31
|
-
// Note: PRODUCT_UPPERCASE_NAME in the CLI bundle is "CODEBUDDY", not "WORKBUDDY".
|
|
32
|
-
function memoryRulesPath() {
|
|
33
|
-
return path.join(configDir(), 'CODEBUDDY.md')
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Project-level memory rules. WorkBuddy walks up from the workspace dir and
|
|
37
|
-
// auto-loads CODEBUDDY.md / AGENTS.md (project scope) into every session.
|
|
38
|
-
function projectInstructionsPath(workspace) {
|
|
39
|
-
return path.join(workspace || process.cwd(), 'AGENTS.md')
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function readJson(file) {
|
|
43
|
-
if (!fs.existsSync(file)) return null
|
|
44
|
-
try {
|
|
45
|
-
return JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
46
|
-
} catch (_) {
|
|
47
|
-
return null
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const MEMORY_BEGIN = '<!-- MIO_INTELLIGENCE_BEGIN -->'
|
|
52
|
-
const MEMORY_END = '<!-- MIO_INTELLIGENCE_END -->'
|
|
53
|
-
const MEMORY_BLOCK = `${MEMORY_BEGIN}
|
|
54
|
-
## Mio Intelligence Layer
|
|
55
|
-
|
|
56
|
-
Mio MCP tools are available. Treat these as mandatory runtime rules for every workspace.
|
|
57
|
-
|
|
58
|
-
- Before designing or implementing a solution, or when unsure about prior decisions, call \`
|
|
59
|
-
- After a decision affecting architecture, deployment, dependencies, or long-term behavior, call \`
|
|
60
|
-
- When a tool call fails, retries, or returns an error, call \`
|
|
61
|
-
- Before a high-risk operation such as deletion, migration, credential change, or production change, call \`
|
|
62
|
-
- At the end of every task, before producing the final answer, call \`
|
|
63
|
-
- When you reuse a prior decision or experience returned by \`
|
|
64
|
-
- If Mio MCP is unavailable, do not block the task. Continue normally and note the missed memory or observation.
|
|
65
|
-
- Do not store secrets, credentials, or raw sensitive content in Mio memory.
|
|
66
|
-
${MEMORY_END}`
|
|
67
|
-
|
|
68
|
-
function ensureBlock(target) {
|
|
69
|
-
let content = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
|
|
70
|
-
const original = content
|
|
71
|
-
const start = content.indexOf(MEMORY_BEGIN)
|
|
72
|
-
if (start !== -1) {
|
|
73
|
-
const end = content.indexOf(MEMORY_END, start)
|
|
74
|
-
content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + MEMORY_END.length)
|
|
75
|
-
content = content.trimEnd()
|
|
76
|
-
}
|
|
77
|
-
if (content) content += '\n\n'
|
|
78
|
-
content += MEMORY_BLOCK + '\n'
|
|
79
|
-
if (content === original) return { changed: false, target, backup: null }
|
|
80
|
-
const backup = `${target}.bak-mio-${Date.now()}`
|
|
81
|
-
if (fs.existsSync(target)) fs.copyFileSync(target, backup)
|
|
82
|
-
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
83
|
-
fs.writeFileSync(target, content, 'utf8')
|
|
84
|
-
return { changed: true, target, backup }
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function isInstalled() {
|
|
88
|
-
const config = readJson(configPath())
|
|
89
|
-
const entry = config && config.mcpServers && config.mcpServers['mio-intelligence']
|
|
90
|
-
if (!entry) return false
|
|
91
|
-
|
|
92
|
-
const approvals = readJson(approvalsPath()) || {}
|
|
93
|
-
const memory = fs.existsSync(memoryRulesPath()) ? fs.readFileSync(memoryRulesPath(), 'utf8') : ''
|
|
94
|
-
return Boolean(
|
|
95
|
-
approvals[approvalKey('mio-intelligence', entry)] &&
|
|
96
|
-
memory.includes(MEMORY_BEGIN) &&
|
|
97
|
-
memory.includes(MEMORY_END)
|
|
98
|
-
)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function approvalKey(name, entry) {
|
|
102
|
-
let input
|
|
103
|
-
if (entry.command) {
|
|
104
|
-
input = `${entry.command || ''}|${(entry.args || []).map(String).sort().join(',')}|${Object.keys(entry.env || {}).sort().join(',')}`
|
|
105
|
-
} else if (entry.url) {
|
|
106
|
-
try {
|
|
107
|
-
input = new URL(entry.url).origin
|
|
108
|
-
} catch (_) {
|
|
109
|
-
input = entry.url
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
input = JSON.stringify(entry)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const hash = crypto.createHash('sha256').update(input).digest('hex')
|
|
116
|
-
return `${hash}::${name}`
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function writeApproval(name, entry) {
|
|
120
|
-
const target = approvalsPath()
|
|
121
|
-
const approvals = readJson(target) || {}
|
|
122
|
-
approvals[approvalKey(name, entry)] = Date.now()
|
|
123
|
-
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
124
|
-
fs.writeFileSync(target, `${JSON.stringify(approvals, null, 2)}\n`, 'utf8')
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function install({ node, serverScript, home, workspace
|
|
128
|
-
const target = configPath()
|
|
129
|
-
const canonical = readJson(target) || {}
|
|
130
|
-
const legacy = readJson(legacyConfigPath()) || {}
|
|
131
|
-
const canonicalServers =
|
|
132
|
-
canonical.mcpServers && typeof canonical.mcpServers === 'object' ? canonical.mcpServers : {}
|
|
133
|
-
const legacyServers =
|
|
134
|
-
legacy.mcpServers && typeof legacy.mcpServers === 'object' ? legacy.mcpServers : {}
|
|
135
|
-
|
|
136
|
-
const config = {
|
|
137
|
-
...legacy,
|
|
138
|
-
...canonical,
|
|
139
|
-
mcpServers: {
|
|
140
|
-
...legacyServers,
|
|
141
|
-
...canonicalServers,
|
|
142
|
-
},
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
fs.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const crypto = require('crypto')
|
|
7
|
+
|
|
8
|
+
function configDir() {
|
|
9
|
+
return (
|
|
10
|
+
process.env.WORKBUDDY_CONFIG_DIR ||
|
|
11
|
+
process.env.CODEBUDDY_CONFIG_DIR ||
|
|
12
|
+
path.join(os.homedir(), '.workbuddy')
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function configPath() {
|
|
17
|
+
return path.join(configDir(), 'mcp.json')
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function legacyConfigPath() {
|
|
21
|
+
return path.join(configDir(), '.mcp.json')
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function approvalsPath() {
|
|
25
|
+
return path.join(configDir(), 'mcp-approvals.json')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// User-level memory rules file. WorkBuddy's codebuddy CLI (a CodeBuddy fork)
|
|
29
|
+
// auto-loads CODEBUDDY.md (or CODEBUDDY.mdc) from the config dir into every
|
|
30
|
+
// session (MemoryLoader user scope -> system-reminder-md / prompt context).
|
|
31
|
+
// Note: PRODUCT_UPPERCASE_NAME in the CLI bundle is "CODEBUDDY", not "WORKBUDDY".
|
|
32
|
+
function memoryRulesPath() {
|
|
33
|
+
return path.join(configDir(), 'CODEBUDDY.md')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Project-level memory rules. WorkBuddy walks up from the workspace dir and
|
|
37
|
+
// auto-loads CODEBUDDY.md / AGENTS.md (project scope) into every session.
|
|
38
|
+
function projectInstructionsPath(workspace) {
|
|
39
|
+
return path.join(workspace || process.cwd(), 'AGENTS.md')
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readJson(file) {
|
|
43
|
+
if (!fs.existsSync(file)) return null
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'))
|
|
46
|
+
} catch (_) {
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const MEMORY_BEGIN = '<!-- MIO_INTELLIGENCE_BEGIN -->'
|
|
52
|
+
const MEMORY_END = '<!-- MIO_INTELLIGENCE_END -->'
|
|
53
|
+
const MEMORY_BLOCK = `${MEMORY_BEGIN}
|
|
54
|
+
## Mio Intelligence Layer
|
|
55
|
+
|
|
56
|
+
Mio MCP tools are available. Treat these as mandatory runtime rules for every workspace.
|
|
57
|
+
|
|
58
|
+
- Before designing or implementing a solution, or when unsure about prior decisions, call \`mcp__mio-intelligence__mio.memory.query\` first. Omit \`project\` unless a specific project is required; Mio derives it from the current Git repository.
|
|
59
|
+
- After a decision affecting architecture, deployment, dependencies, or long-term behavior, call \`mcp__mio-intelligence__mio.memory.record\` with \`kind\`: \`decision\`, \`context\`, \`problem\`, or \`note\`.
|
|
60
|
+
- When a tool call fails, retries, or returns an error, call \`mcp__mio-intelligence__mio.observer.ingest\` with a stable \`trace_id\`, relevant \`event_type\`, \`payload\`, and \`outcome\`.
|
|
61
|
+
- Before a high-risk operation such as deletion, migration, credential change, or production change, call \`mcp__mio-intelligence__mio.policy.check\` and follow its suggestion when available.
|
|
62
|
+
- At the end of every task, before producing the final answer, call \`mcp__mio-intelligence__mio.observer.ingest\` with \`event_type\` = \`task_outcome\`, a stable \`trace_id\`, \`outcome\` = \`success\` / \`failure\` / \`aborted\`, and a brief non-sensitive \`payload\`. If Mio is unavailable, do not block the final answer.
|
|
63
|
+
- When you reuse a prior decision or experience returned by \`mcp__mio-intelligence__mio.memory.query\` and it changes your approach or outcome, call \`mcp__mio-intelligence__mio.experience.reuse\` with \`sourceAgent\`, \`targetAgent\`, \`experienceId\`, \`reuse\`, \`behaviorChanged\`, and \`outcomeImproved\`.
|
|
64
|
+
- If Mio MCP is unavailable, do not block the task. Continue normally and note the missed memory or observation.
|
|
65
|
+
- Do not store secrets, credentials, or raw sensitive content in Mio memory.
|
|
66
|
+
${MEMORY_END}`
|
|
67
|
+
|
|
68
|
+
function ensureBlock(target) {
|
|
69
|
+
let content = fs.existsSync(target) ? fs.readFileSync(target, 'utf8') : ''
|
|
70
|
+
const original = content
|
|
71
|
+
const start = content.indexOf(MEMORY_BEGIN)
|
|
72
|
+
if (start !== -1) {
|
|
73
|
+
const end = content.indexOf(MEMORY_END, start)
|
|
74
|
+
content = end === -1 ? content.slice(0, start) : content.slice(0, start) + content.slice(end + MEMORY_END.length)
|
|
75
|
+
content = content.trimEnd()
|
|
76
|
+
}
|
|
77
|
+
if (content) content += '\n\n'
|
|
78
|
+
content += MEMORY_BLOCK + '\n'
|
|
79
|
+
if (content === original) return { changed: false, target, backup: null }
|
|
80
|
+
const backup = `${target}.bak-mio-${Date.now()}`
|
|
81
|
+
if (fs.existsSync(target)) fs.copyFileSync(target, backup)
|
|
82
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
83
|
+
fs.writeFileSync(target, content, 'utf8')
|
|
84
|
+
return { changed: true, target, backup }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isInstalled() {
|
|
88
|
+
const config = readJson(configPath())
|
|
89
|
+
const entry = config && config.mcpServers && config.mcpServers['mio-intelligence']
|
|
90
|
+
if (!entry) return false
|
|
91
|
+
|
|
92
|
+
const approvals = readJson(approvalsPath()) || {}
|
|
93
|
+
const memory = fs.existsSync(memoryRulesPath()) ? fs.readFileSync(memoryRulesPath(), 'utf8') : ''
|
|
94
|
+
return Boolean(
|
|
95
|
+
approvals[approvalKey('mio-intelligence', entry)] &&
|
|
96
|
+
memory.includes(MEMORY_BEGIN) &&
|
|
97
|
+
memory.includes(MEMORY_END)
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function approvalKey(name, entry) {
|
|
102
|
+
let input
|
|
103
|
+
if (entry.command) {
|
|
104
|
+
input = `${entry.command || ''}|${(entry.args || []).map(String).sort().join(',')}|${Object.keys(entry.env || {}).sort().join(',')}`
|
|
105
|
+
} else if (entry.url) {
|
|
106
|
+
try {
|
|
107
|
+
input = new URL(entry.url).origin
|
|
108
|
+
} catch (_) {
|
|
109
|
+
input = entry.url
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
input = JSON.stringify(entry)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hash = crypto.createHash('sha256').update(input).digest('hex')
|
|
116
|
+
return `${hash}::${name}`
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function writeApproval(name, entry) {
|
|
120
|
+
const target = approvalsPath()
|
|
121
|
+
const approvals = readJson(target) || {}
|
|
122
|
+
approvals[approvalKey(name, entry)] = Date.now()
|
|
123
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
124
|
+
fs.writeFileSync(target, `${JSON.stringify(approvals, null, 2)}\n`, 'utf8')
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function install({ node, serverScript, home, workspace }) {
|
|
128
|
+
const target = configPath()
|
|
129
|
+
const canonical = readJson(target) || {}
|
|
130
|
+
const legacy = readJson(legacyConfigPath()) || {}
|
|
131
|
+
const canonicalServers =
|
|
132
|
+
canonical.mcpServers && typeof canonical.mcpServers === 'object' ? canonical.mcpServers : {}
|
|
133
|
+
const legacyServers =
|
|
134
|
+
legacy.mcpServers && typeof legacy.mcpServers === 'object' ? legacy.mcpServers : {}
|
|
135
|
+
|
|
136
|
+
const config = {
|
|
137
|
+
...legacy,
|
|
138
|
+
...canonical,
|
|
139
|
+
mcpServers: {
|
|
140
|
+
...legacyServers,
|
|
141
|
+
...canonicalServers,
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// No project/workspace here: the MCP server derives the project from its own
|
|
146
|
+
// git cwd. A baked value from install-time cwd overrides that derivation and
|
|
147
|
+
// mis-attributes every memory write for the host's lifetime.
|
|
148
|
+
const context = JSON.stringify({
|
|
149
|
+
agentId: 'workbuddy',
|
|
150
|
+
sessionId: 'workbuddy-session',
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
config.mcpServers = {
|
|
154
|
+
...config.mcpServers,
|
|
155
|
+
'mio-intelligence': {
|
|
156
|
+
command: node,
|
|
157
|
+
args: [serverScript],
|
|
158
|
+
env: {
|
|
159
|
+
MIO_DATA_DIR: home,
|
|
160
|
+
MIO_CONTEXT: context,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
writeApproval('mio-intelligence', config.mcpServers['mio-intelligence'])
|
|
166
|
+
|
|
167
|
+
const backup = `${target}.bak-mio-${Date.now()}`
|
|
168
|
+
if (fs.existsSync(target)) {
|
|
169
|
+
fs.copyFileSync(target, backup)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
173
|
+
fs.writeFileSync(target, `${JSON.stringify(config, null, 2)}\n`, 'utf8')
|
|
174
|
+
|
|
175
|
+
const userRules = ensureBlock(memoryRulesPath())
|
|
176
|
+
let projectRules = null
|
|
177
|
+
const workspaceDir = workspace || process.cwd()
|
|
178
|
+
if (workspaceDir && path.isAbsolute(workspaceDir)) {
|
|
179
|
+
projectRules = ensureBlock(projectInstructionsPath(workspaceDir))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const changed = userRules.changed || Boolean(projectRules && projectRules.changed)
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
changed,
|
|
186
|
+
configPath: target,
|
|
187
|
+
approvalsPath: approvalsPath(),
|
|
188
|
+
memoryRulesPath: userRules.target,
|
|
189
|
+
projectInstructionsPath: projectRules ? projectRules.target : null,
|
|
190
|
+
userRulesChanged: userRules.changed,
|
|
191
|
+
projectRulesChanged: projectRules ? projectRules.changed : false,
|
|
192
|
+
backup,
|
|
193
|
+
message: 'WorkBuddy Mio MCP installed, approved, and Mio rules injected into CODEBUDDY.md (auto-loaded user memory) and the workspace AGENTS.md. Restart WorkBuddy to load them.',
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
module.exports = { configDir, configPath, approvalsPath, memoryRulesPath, projectInstructionsPath, approvalKey, isInstalled, install }
|
package/bin/mio.js
CHANGED
|
@@ -23,10 +23,10 @@ const { createEvolutionCutoverTools } = require('../server/evolution-cutover.js'
|
|
|
23
23
|
const { createMemoryStore } = require('../server/memory-store.js')
|
|
24
24
|
const { createExperienceStore } = require('../server/experience-store.js')
|
|
25
25
|
const { createPolicyStore } = require('../server/policy-store.js')
|
|
26
|
-
const { CreativityEngine } = require('../server/creativity-engine.js')
|
|
26
|
+
const { CreativityEngine, sourcesFromInsights } = require('../server/creativity-engine.js')
|
|
27
27
|
const { createAgentStore } = require('../server/agent-store.js')
|
|
28
28
|
const { createEvaluationStore } = require('../server/evaluation-store.js')
|
|
29
|
-
const { createInsightStore } = require('../server/insight-store.js')
|
|
29
|
+
const { createInsightStore, isInsightAvailable } = require('../server/insight-store.js')
|
|
30
30
|
const { createObserverStore, isObserverAvailable } = require('../server/observer-store.js')
|
|
31
31
|
const { loadPhase0, renderPhase0Markdown } = require('../server/mio-intelligence-mcp/phase0.js')
|
|
32
32
|
const { listHostCapabilities } = require('../server/host-capabilities.js')
|
|
@@ -564,7 +564,9 @@ function digestCommand(args, useJson) {
|
|
|
564
564
|
let state = {}
|
|
565
565
|
try {
|
|
566
566
|
state = JSON.parse(fs.readFileSync(stateFile, 'utf8'))
|
|
567
|
-
} catch (
|
|
567
|
+
} catch (err) {
|
|
568
|
+
throw new Error(`Failed to parse ${stateFile}: ${err.message}`)
|
|
569
|
+
}
|
|
568
570
|
state.contexts = state.contexts || {}
|
|
569
571
|
const stamp = new Date().toLocaleString('zh-CN', { hour12: false })
|
|
570
572
|
|
|
@@ -595,6 +597,7 @@ function digestCommand(args, useJson) {
|
|
|
595
597
|
}
|
|
596
598
|
|
|
597
599
|
let written = 0
|
|
600
|
+
const writeBackFailures = []
|
|
598
601
|
for (const cwd of targetCwds) {
|
|
599
602
|
const line = projectLine.get(path.basename(cwd).toLowerCase())
|
|
600
603
|
if (!line) continue
|
|
@@ -602,13 +605,18 @@ function digestCommand(args, useJson) {
|
|
|
602
605
|
observer.updateProjectContext(state, cwd, stamp + ' | ' + line, 'CLAUDE.md')
|
|
603
606
|
observer.updateProjectContext(state, cwd, stamp + ' | ' + line, 'AGENTS.md')
|
|
604
607
|
written += 1
|
|
605
|
-
} catch (
|
|
608
|
+
} catch (err) {
|
|
609
|
+
const wsName = path.basename(cwd)
|
|
610
|
+
console.error(`Write-back failed for ${wsName}:`, err.message || err)
|
|
611
|
+
writeBackFailures.push({ workspace: wsName, error: err.message || String(err) })
|
|
612
|
+
}
|
|
606
613
|
}
|
|
607
614
|
fs.mkdirSync(MIO_HOME, { recursive: true })
|
|
608
615
|
fs.writeFileSync(stateFile, JSON.stringify(state) + '\n', 'utf8')
|
|
609
616
|
report.writeBack = {
|
|
610
617
|
workspaces: written,
|
|
611
618
|
skipped: Object.keys(state.contexts).length - written,
|
|
619
|
+
failures: writeBackFailures,
|
|
612
620
|
projects: [...projectLine.values()],
|
|
613
621
|
}
|
|
614
622
|
}
|
|
@@ -626,6 +634,10 @@ function digestCommand(args, useJson) {
|
|
|
626
634
|
console.log(
|
|
627
635
|
`Write-back: ${report.writeBack.workspaces} workspace(s), ${report.writeBack.skipped} skipped (no digest data)`,
|
|
628
636
|
)
|
|
637
|
+
if (report.writeBack.failures && report.writeBack.failures.length > 0) {
|
|
638
|
+
console.error(` ${report.writeBack.failures.length} write-back failure(s):`)
|
|
639
|
+
for (const f of report.writeBack.failures) console.error(` - ${f.workspace}: ${f.error}`)
|
|
640
|
+
}
|
|
629
641
|
for (const line of report.writeBack.projects) console.log(` - ${line}`)
|
|
630
642
|
}
|
|
631
643
|
}
|
|
@@ -2060,8 +2072,9 @@ function creativityUsage(write = console.log) {
|
|
|
2060
2072
|
|
|
2061
2073
|
Options:
|
|
2062
2074
|
--status name Filter list by status
|
|
2063
|
-
--limit N Max results for list / ferment (default 20 / 5)
|
|
2075
|
+
--limit N Max results for list / ferment (default 20 / 5); --limit 0 lists everything
|
|
2064
2076
|
--source "name|content" A concept source for generate; repeat 2+ times
|
|
2077
|
+
--from-insights Seed generate's sources from stored insights (needs @akemi-mio/insight)
|
|
2065
2078
|
--strategy explore|signal|stable Generation strategy (auto-selected if omitted)
|
|
2066
2079
|
--json Machine-readable output (same shape as the mio.creativity.* tools)
|
|
2067
2080
|
|
|
@@ -2069,6 +2082,7 @@ Examples:
|
|
|
2069
2082
|
mio creativity status
|
|
2070
2083
|
mio creativity list --status rejected --limit 10
|
|
2071
2084
|
mio creativity generate --source "auth|token rotation" --source "cache|write-through"
|
|
2085
|
+
mio creativity generate --from-insights --source "auth|token rotation"
|
|
2072
2086
|
|
|
2073
2087
|
generate/ferment call an LLM using the shared client. Configure it with
|
|
2074
2088
|
LLM_API_URL / LLM_KEY / LLM_CHAT_MODEL (the same environment the MCP server
|
|
@@ -2079,7 +2093,7 @@ reads). A local Ollama works:
|
|
|
2079
2093
|
function printCreativityStatus(result) {
|
|
2080
2094
|
console.log('Creativity engine:')
|
|
2081
2095
|
console.log(` hypotheses: ${result.hypotheses} combos: ${result.combos} experiments: ${result.experiments}`)
|
|
2082
|
-
console.log(` active: ${result.active} validated: ${result.validated} rejected: ${result.rejected}`)
|
|
2096
|
+
console.log(` active: ${result.active} validated: ${result.validated} rejected: ${result.rejected} draft: ${result.draft}`)
|
|
2083
2097
|
if (result.recentIdeas.length === 0) {
|
|
2084
2098
|
console.log('No hypotheses yet.')
|
|
2085
2099
|
return
|
|
@@ -2136,12 +2150,56 @@ function warnIfLlmUnconfigured() {
|
|
|
2136
2150
|
console.log('(or set LLM_API_URL / LLM_KEY / LLM_CHAT_MODEL in the environment)')
|
|
2137
2151
|
}
|
|
2138
2152
|
|
|
2153
|
+
// Failed LLM calls used to disappear: `generate` swallowed them and `ferment`
|
|
2154
|
+
// reported "Nothing fermented", so a dead endpoint looked exactly like "no new
|
|
2155
|
+
// ideas". Both now surface them; the JSON shapes carry `errors` as well.
|
|
2156
|
+
function printCreativityErrors(errors, label) {
|
|
2157
|
+
if (!Array.isArray(errors) || errors.length === 0) return
|
|
2158
|
+
console.log(`\n${errors.length} ${label}(s) failed:`)
|
|
2159
|
+
for (const err of errors) {
|
|
2160
|
+
const who = err.pair ? err.pair.join(' + ') : (err.title || err.id)
|
|
2161
|
+
console.log(`- ${who}: ${err.error}`)
|
|
2162
|
+
}
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2139
2165
|
function creativityGenerateCommand(args, useJson) {
|
|
2140
2166
|
const flags = args.slice(2)
|
|
2141
|
-
|
|
2167
|
+
let sources = parseSources(flags)
|
|
2168
|
+
|
|
2169
|
+
// `--from-insights` seeds the mixer with whatever the insight engine has
|
|
2170
|
+
// already noticed. It reads the *CLI's* store (global MIO_HOME), the same
|
|
2171
|
+
// one `mio insight list` shows, and merges with any explicit --source.
|
|
2172
|
+
if (flagPresent(flags, '--from-insights')) {
|
|
2173
|
+
if (!isInsightAvailable()) {
|
|
2174
|
+
console.error('mio creativity generate --from-insights requires the optional package @akemi-mio/insight')
|
|
2175
|
+
process.exitCode = 1
|
|
2176
|
+
return
|
|
2177
|
+
}
|
|
2178
|
+
let seeds
|
|
2179
|
+
try {
|
|
2180
|
+
seeds = sourcesFromInsights(cliInsightStore().list({}))
|
|
2181
|
+
} catch (error) {
|
|
2182
|
+
console.error(error.message || error)
|
|
2183
|
+
process.exitCode = 1
|
|
2184
|
+
return
|
|
2185
|
+
}
|
|
2186
|
+
if (seeds.length === 0) {
|
|
2187
|
+
console.error('mio creativity generate --from-insights: no stored insights yet (run `mio insight generate` first)')
|
|
2188
|
+
process.exitCode = 1
|
|
2189
|
+
return
|
|
2190
|
+
}
|
|
2191
|
+
sources = sources.concat(seeds)
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2142
2194
|
if (sources.length < 2) {
|
|
2143
|
-
|
|
2144
|
-
|
|
2195
|
+
if (flagPresent(flags, '--from-insights')) {
|
|
2196
|
+
console.error(`mio creativity generate needs at least 2 concept sources; --from-insights only supplied ${sources.length}.`)
|
|
2197
|
+
console.error('Add more --source "name|content" arguments, or generate more insights first.')
|
|
2198
|
+
} else {
|
|
2199
|
+
console.error('mio creativity generate requires at least two --source "name|content" arguments')
|
|
2200
|
+
console.error('Example: mio creativity generate --source "auth|token rotation" --source "cache|write-through cache"')
|
|
2201
|
+
console.error('Or seed them with --from-insights (uses stored insights as concepts).')
|
|
2202
|
+
}
|
|
2145
2203
|
process.exitCode = 1
|
|
2146
2204
|
return
|
|
2147
2205
|
}
|
|
@@ -2170,6 +2228,7 @@ function creativityGenerateCommand(args, useJson) {
|
|
|
2170
2228
|
console.log(` novelty=${idea.novelty} feasibility=${idea.feasibility} impact=${idea.impact} | ${idea.id}`)
|
|
2171
2229
|
if (idea.rejectionReason) console.log(` rejected: ${idea.rejectionReason}`)
|
|
2172
2230
|
})
|
|
2231
|
+
printCreativityErrors(resolved.errors, 'pair')
|
|
2173
2232
|
})
|
|
2174
2233
|
}
|
|
2175
2234
|
|
|
@@ -2191,12 +2250,16 @@ function creativityFermentCommand(args, useJson) {
|
|
|
2191
2250
|
if (useJson) return jsonOrText(resolved, true)
|
|
2192
2251
|
if (resolved.fermented === 0) {
|
|
2193
2252
|
console.log(`Nothing fermented: ${resolved.reason || 'no eligible hypotheses'}`)
|
|
2253
|
+
// Every review can fail while there was something to review; without
|
|
2254
|
+
// this the endpoint outage read as "the store is empty".
|
|
2255
|
+
printCreativityErrors(resolved.errors, 'review')
|
|
2194
2256
|
return
|
|
2195
2257
|
}
|
|
2196
2258
|
console.log(`Fermented ${resolved.fermented} hypothesis/hypotheses`)
|
|
2197
2259
|
for (const r of resolved.results || []) {
|
|
2198
2260
|
console.log(`- ${r.title || r.id}: ${r.verdict || 'updated'}${r.reason ? ` — ${r.reason}` : ''}`)
|
|
2199
2261
|
}
|
|
2262
|
+
printCreativityErrors(resolved.errors, 'review')
|
|
2200
2263
|
})
|
|
2201
2264
|
}
|
|
2202
2265
|
|
|
@@ -3136,22 +3199,33 @@ function installCommand(host, useJson) {
|
|
|
3136
3199
|
}
|
|
3137
3200
|
writeConfig(config)
|
|
3138
3201
|
}
|
|
3202
|
+
let observerCycleOk = true
|
|
3139
3203
|
try {
|
|
3140
3204
|
observer.runCycle(MIO_HOME, observer.loadState(MIO_HOME), observer.createSinks(MIO_HOME))
|
|
3141
|
-
} catch (
|
|
3205
|
+
} catch (err) {
|
|
3206
|
+
console.error('Observer cycle failed during install:', err.message || err)
|
|
3207
|
+
observerCycleOk = false
|
|
3208
|
+
}
|
|
3142
3209
|
let observerStarted = false
|
|
3143
3210
|
try {
|
|
3144
3211
|
observerStarted = observer.startBackground(MIO_HOME).started
|
|
3145
|
-
} catch (
|
|
3212
|
+
} catch (err) {
|
|
3213
|
+
console.error('Observer background startup failed during install:', err.message || err)
|
|
3214
|
+
observerStarted = false
|
|
3215
|
+
}
|
|
3146
3216
|
if (useJson) {
|
|
3147
3217
|
result.observer = {
|
|
3148
3218
|
running: observer.isRunning(MIO_HOME),
|
|
3149
3219
|
started: observerStarted,
|
|
3220
|
+
cycleOk: observerCycleOk,
|
|
3150
3221
|
}
|
|
3151
3222
|
jsonOrText(result, true)
|
|
3152
3223
|
return
|
|
3153
3224
|
}
|
|
3154
3225
|
console.log(result.message || `${host} adapter completed.`)
|
|
3226
|
+
if (!observerCycleOk) {
|
|
3227
|
+
console.error('Warning: observer cycle failed during install; run `mio observe --once` manually.')
|
|
3228
|
+
}
|
|
3155
3229
|
console.log(
|
|
3156
3230
|
observer.isRunning(MIO_HOME)
|
|
3157
3231
|
? 'Observer daemon running.'
|
|
@@ -3160,6 +3234,13 @@ function installCommand(host, useJson) {
|
|
|
3160
3234
|
}
|
|
3161
3235
|
|
|
3162
3236
|
function mcpCommand() {
|
|
3237
|
+
// The MCP server resolves its data dir as `MIO_DATA_DIR || <cwd>/.mio-intelligence`,
|
|
3238
|
+
// while every CLI subcommand reads the global MIO_HOME. Left alone, `mio mcp`
|
|
3239
|
+
// launched from a scratch directory built a second, empty store there and
|
|
3240
|
+
// `mio.creativity.list` / `mio.recall` disagreed with `mio creativity list` /
|
|
3241
|
+
// `mio recall`. Pinning it makes the stdio server see the same data as the
|
|
3242
|
+
// terminal; an explicit MIO_DATA_DIR still wins (adapters set one on purpose).
|
|
3243
|
+
if (!process.env.MIO_DATA_DIR) process.env.MIO_DATA_DIR = MIO_HOME
|
|
3163
3244
|
require(SERVER_SCRIPT)
|
|
3164
3245
|
}
|
|
3165
3246
|
|
|
@@ -3289,7 +3370,7 @@ Usage:
|
|
|
3289
3370
|
mio experience reuse --source-agent A --target-agent B --experience-id X Record a reuse
|
|
3290
3371
|
mio creativity status Show creativity hypothesis counts and recent top ideas
|
|
3291
3372
|
mio creativity list List creativity hypotheses (--status active|validated|rejected|draft, --limit N)
|
|
3292
|
-
mio creativity generate Generate hypotheses from 2+ --source "name|content" (calls an LLM)
|
|
3373
|
+
mio creativity generate Generate hypotheses from 2+ --source "name|content" (or --from-insights; calls an LLM)
|
|
3293
3374
|
mio creativity ferment Review and refine active hypotheses (calls an LLM)
|
|
3294
3375
|
mio insight status Insight counts: total, reported, unreported, high-value
|
|
3295
3376
|
mio insight list List insights (--unreported, --min-score N, --detector X, --limit N)
|